Merge branch 'master' into copy-auto-increment
This commit is contained in:
commit
525a80d62e
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
@ -10,10 +10,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up Go 1.14
|
- name: Set up Go 1.15
|
||||||
uses: actions/setup-go@v1
|
uses: actions/setup-go@v1
|
||||||
with:
|
with:
|
||||||
go-version: 1.14
|
go-version: 1.15
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: script/cibuild
|
run: script/cibuild
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
FROM golang:1.14.7
|
FROM golang:1.15.6
|
||||||
|
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
RUN apt-get install -y ruby ruby-dev rubygems build-essential
|
RUN apt-get install -y ruby ruby-dev rubygems build-essential
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.14.7
|
FROM golang:1.15.6
|
||||||
LABEL maintainer="github@github.com"
|
LABEL maintainer="github@github.com"
|
||||||
|
|
||||||
RUN apt-get update
|
RUN apt-get update
|
||||||
|
@ -65,6 +65,7 @@ Also see:
|
|||||||
- [the fine print](doc/the-fine-print.md)
|
- [the fine print](doc/the-fine-print.md)
|
||||||
- [Community questions](https://github.com/github/gh-ost/issues?q=label%3Aquestion)
|
- [Community questions](https://github.com/github/gh-ost/issues?q=label%3Aquestion)
|
||||||
- [Using `gh-ost` on AWS RDS](doc/rds.md)
|
- [Using `gh-ost` on AWS RDS](doc/rds.md)
|
||||||
|
- [Using `gh-ost` on Azure Database for MySQL](doc/azure.md)
|
||||||
|
|
||||||
## What's in a name?
|
## What's in a name?
|
||||||
|
|
||||||
|
26
doc/azure.md
Normal file
26
doc/azure.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
`gh-ost` has been updated to work with Azure Database for MySQL however due to GitHub does not use it, this documentation is community driven so if you find a bug please [open an issue][new_issue]!
|
||||||
|
|
||||||
|
# Azure Database for MySQL
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
- `gh-ost` runs should be setup use [`--assume-rbr`][assume_rbr_docs] and use `binlog_row_image=FULL`.
|
||||||
|
- Azure Database for MySQL does not use same user name suffix for master and replica, so master host, user and password need to be pointed out.
|
||||||
|
|
||||||
|
## Step
|
||||||
|
1. Change the replica server's `binlog_row_image` from `MINIMAL` to `FULL`. See [guide](https://docs.microsoft.com/en-us/azure/mysql/howto-server-parameters) on Azure document.
|
||||||
|
2. Use your `gh-ost` always with additional 5 parameter
|
||||||
|
```{bash}
|
||||||
|
gh-ost \
|
||||||
|
--azure \
|
||||||
|
--assume-master-host=master-server-dns-name \
|
||||||
|
--master-user="master-user-name" \
|
||||||
|
--master-password="master-password" \
|
||||||
|
--assume-rbr \
|
||||||
|
[-- other paramters you need]
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
[new_issue]: https://github.com/github/gh-ost/issues/new
|
||||||
|
[assume_rbr_docs]: https://github.com/github/gh-ost/blob/master/doc/command-line-flags.md#assume-rbr
|
||||||
|
[migrate_test_on_replica_docs]: https://github.com/github/gh-ost/blob/master/doc/cheatsheet.md#c-migratetest-on-replica
|
@ -6,6 +6,10 @@ A more in-depth discussion of various `gh-ost` command line flags: implementatio
|
|||||||
|
|
||||||
Add this flag when executing on Aliyun RDS.
|
Add this flag when executing on Aliyun RDS.
|
||||||
|
|
||||||
|
### azure
|
||||||
|
|
||||||
|
Add this flag when executing on Azure Database for MySQL.
|
||||||
|
|
||||||
### allow-master-master
|
### allow-master-master
|
||||||
|
|
||||||
See [`--assume-master-host`](#assume-master-host).
|
See [`--assume-master-host`](#assume-master-host).
|
||||||
|
@ -41,6 +41,7 @@ The `SUPER` privilege is required for `STOP SLAVE`, `START SLAVE` operations. Th
|
|||||||
- Amazon RDS works, but has its own [limitations](rds.md).
|
- Amazon RDS works, but has its own [limitations](rds.md).
|
||||||
- Google Cloud SQL works, `--gcp` flag required.
|
- Google Cloud SQL works, `--gcp` flag required.
|
||||||
- Aliyun RDS works, `--aliyun-rds` flag required.
|
- Aliyun RDS works, `--aliyun-rds` flag required.
|
||||||
|
- Azure Database for MySQL works, `--azure` flag required, and have detailed document about it. (azure.md)
|
||||||
|
|
||||||
- Multisource is not supported when migrating via replica. It _should_ work (but never tested) when connecting directly to master (`--allow-on-master`)
|
- Multisource is not supported when migrating via replica. It _should_ work (but never tested) when connecting directly to master (`--allow-on-master`)
|
||||||
|
|
||||||
|
@ -97,6 +97,7 @@ type MigrationContext struct {
|
|||||||
DiscardForeignKeys bool
|
DiscardForeignKeys bool
|
||||||
AliyunRDS bool
|
AliyunRDS bool
|
||||||
GoogleCloudPlatform bool
|
GoogleCloudPlatform bool
|
||||||
|
AzureMySQL bool
|
||||||
|
|
||||||
config ContextConfig
|
config ContextConfig
|
||||||
configMutex *sync.Mutex
|
configMutex *sync.Mutex
|
||||||
|
@ -75,7 +75,8 @@ func ValidateConnection(db *gosql.DB, connectionConfig *mysql.ConnectionConfig,
|
|||||||
}
|
}
|
||||||
// AliyunRDS set users port to "NULL", replace it by gh-ost param
|
// AliyunRDS set users port to "NULL", replace it by gh-ost param
|
||||||
// GCP set users port to "NULL", replace it by gh-ost param
|
// GCP set users port to "NULL", replace it by gh-ost param
|
||||||
if migrationContext.AliyunRDS || migrationContext.GoogleCloudPlatform {
|
// Azure MySQL set users port to a different value by design, replace it by gh-ost para
|
||||||
|
if migrationContext.AliyunRDS || migrationContext.GoogleCloudPlatform || migrationContext.AzureMySQL {
|
||||||
port = connectionConfig.Key.Port
|
port = connectionConfig.Key.Port
|
||||||
} else {
|
} else {
|
||||||
portQuery := `select @@global.port`
|
portQuery := `select @@global.port`
|
||||||
|
@ -79,6 +79,7 @@ func main() {
|
|||||||
flag.BoolVar(&migrationContext.SkipStrictMode, "skip-strict-mode", false, "explicitly tell gh-ost binlog applier not to enforce strict sql mode")
|
flag.BoolVar(&migrationContext.SkipStrictMode, "skip-strict-mode", false, "explicitly tell gh-ost binlog applier not to enforce strict sql mode")
|
||||||
flag.BoolVar(&migrationContext.AliyunRDS, "aliyun-rds", false, "set to 'true' when you execute on Aliyun RDS.")
|
flag.BoolVar(&migrationContext.AliyunRDS, "aliyun-rds", false, "set to 'true' when you execute on Aliyun RDS.")
|
||||||
flag.BoolVar(&migrationContext.GoogleCloudPlatform, "gcp", false, "set to 'true' when you execute on a 1st generation Google Cloud Platform (GCP).")
|
flag.BoolVar(&migrationContext.GoogleCloudPlatform, "gcp", false, "set to 'true' when you execute on a 1st generation Google Cloud Platform (GCP).")
|
||||||
|
flag.BoolVar(&migrationContext.AzureMySQL, "azure", false, "set to 'true' when you execute on Azure Database on MySQL.")
|
||||||
|
|
||||||
executeFlag := flag.Bool("execute", false, "actually execute the alter & migrate the table. Default is noop: do some tests and exit")
|
executeFlag := flag.Bool("execute", false, "actually execute the alter & migrate the table. Default is noop: do some tests and exit")
|
||||||
flag.BoolVar(&migrationContext.TestOnReplica, "test-on-replica", false, "Have the migration run on a replica, not on the master. At the end of migration replication is stopped, and tables are swapped and immediately swap-revert. Replication remains stopped and you can compare the two tables for building trust")
|
flag.BoolVar(&migrationContext.TestOnReplica, "test-on-replica", false, "Have the migration run on a replica, not on the master. At the end of migration replication is stopped, and tables are swapped and immediately swap-revert. Replication remains stopped and you can compare the two tables for building trust")
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/github/gh-ost/go/sql"
|
"github.com/github/gh-ost/go/sql"
|
||||||
|
|
||||||
"github.com/outbrain/golib/sqlutils"
|
"github.com/outbrain/golib/sqlutils"
|
||||||
|
"sync"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -88,7 +89,7 @@ func (this *Applier) InitDBConnections() (err error) {
|
|||||||
if err := this.validateAndReadTimeZone(); err != nil {
|
if err := this.validateAndReadTimeZone(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !this.migrationContext.AliyunRDS && !this.migrationContext.GoogleCloudPlatform {
|
if !this.migrationContext.AliyunRDS && !this.migrationContext.GoogleCloudPlatform && !this.migrationContext.AzureMySQL {
|
||||||
if impliedKey, err := mysql.GetInstanceKey(this.db); err != nil {
|
if impliedKey, err := mysql.GetInstanceKey(this.db); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
@ -806,7 +807,7 @@ func (this *Applier) CreateAtomicCutOverSentryTable() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AtomicCutOverMagicLock
|
// AtomicCutOverMagicLock
|
||||||
func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocked chan<- error, okToUnlockTable <-chan bool, tableUnlocked chan<- error) error {
|
func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocked chan<- error, okToUnlockTable <-chan bool, tableUnlocked chan<- error, dropCutOverSentryTableOnce *sync.Once) error {
|
||||||
tx, err := this.db.Begin()
|
tx, err := this.db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tableLocked <- err
|
tableLocked <- err
|
||||||
@ -884,10 +885,13 @@ func (this *Applier) AtomicCutOverMagicLock(sessionIdChan chan int64, tableLocke
|
|||||||
sql.EscapeName(this.migrationContext.DatabaseName),
|
sql.EscapeName(this.migrationContext.DatabaseName),
|
||||||
sql.EscapeName(this.migrationContext.GetOldTableName()),
|
sql.EscapeName(this.migrationContext.GetOldTableName()),
|
||||||
)
|
)
|
||||||
if _, err := tx.Exec(query); err != nil {
|
|
||||||
this.migrationContext.Log.Errore(err)
|
dropCutOverSentryTableOnce.Do(func() {
|
||||||
// We DO NOT return here because we must `UNLOCK TABLES`!
|
if _, err := tx.Exec(query); err != nil {
|
||||||
}
|
this.migrationContext.Log.Errore(err)
|
||||||
|
// We DO NOT return here because we must `UNLOCK TABLES`!
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
// Tables still locked
|
// Tables still locked
|
||||||
this.migrationContext.Log.Infof("Releasing lock from %s.%s, %s.%s",
|
this.migrationContext.Log.Infof("Releasing lock from %s.%s, %s.%s",
|
||||||
|
@ -52,7 +52,7 @@ func (this *Inspector) InitDBConnections() (err error) {
|
|||||||
if err := this.validateConnection(); err != nil {
|
if err := this.validateConnection(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !this.migrationContext.AliyunRDS && !this.migrationContext.GoogleCloudPlatform {
|
if !this.migrationContext.AliyunRDS && !this.migrationContext.GoogleCloudPlatform && !this.migrationContext.AzureMySQL {
|
||||||
if impliedKey, err := mysql.GetInstanceKey(this.db); err != nil {
|
if impliedKey, err := mysql.GetInstanceKey(this.db); err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -606,9 +607,12 @@ func (this *Migrator) atomicCutOver() (err error) {
|
|||||||
defer atomic.StoreInt64(&this.migrationContext.InCutOverCriticalSectionFlag, 0)
|
defer atomic.StoreInt64(&this.migrationContext.InCutOverCriticalSectionFlag, 0)
|
||||||
|
|
||||||
okToUnlockTable := make(chan bool, 4)
|
okToUnlockTable := make(chan bool, 4)
|
||||||
|
var dropCutOverSentryTableOnce sync.Once
|
||||||
defer func() {
|
defer func() {
|
||||||
okToUnlockTable <- true
|
okToUnlockTable <- true
|
||||||
this.applier.DropAtomicCutOverSentryTableIfExists()
|
dropCutOverSentryTableOnce.Do(func() {
|
||||||
|
this.applier.DropAtomicCutOverSentryTableIfExists()
|
||||||
|
})
|
||||||
}()
|
}()
|
||||||
|
|
||||||
atomic.StoreInt64(&this.migrationContext.AllEventsUpToLockProcessedInjectedFlag, 0)
|
atomic.StoreInt64(&this.migrationContext.AllEventsUpToLockProcessedInjectedFlag, 0)
|
||||||
@ -617,7 +621,7 @@ func (this *Migrator) atomicCutOver() (err error) {
|
|||||||
tableLocked := make(chan error, 2)
|
tableLocked := make(chan error, 2)
|
||||||
tableUnlocked := make(chan error, 2)
|
tableUnlocked := make(chan error, 2)
|
||||||
go func() {
|
go func() {
|
||||||
if err := this.applier.AtomicCutOverMagicLock(lockOriginalSessionIdChan, tableLocked, okToUnlockTable, tableUnlocked); err != nil {
|
if err := this.applier.AtomicCutOverMagicLock(lockOriginalSessionIdChan, tableLocked, okToUnlockTable, tableUnlocked, &dropCutOverSentryTableOnce); err != nil {
|
||||||
this.migrationContext.Log.Errore(err)
|
this.migrationContext.Log.Errore(err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@ -737,7 +741,7 @@ func (this *Migrator) initiateInspector() (err error) {
|
|||||||
this.migrationContext.Log.Infof("Master found to be %+v", *this.migrationContext.ApplierConnectionConfig.ImpliedKey)
|
this.migrationContext.Log.Infof("Master found to be %+v", *this.migrationContext.ApplierConnectionConfig.ImpliedKey)
|
||||||
} else {
|
} else {
|
||||||
// Forced master host.
|
// Forced master host.
|
||||||
key, err := mysql.ParseRawInstanceKeyLoose(this.migrationContext.AssumeMasterHostname)
|
key, err := mysql.ParseInstanceKey(this.migrationContext.AssumeMasterHostname)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ package mysql
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -15,6 +16,13 @@ const (
|
|||||||
DefaultInstancePort = 3306
|
DefaultInstancePort = 3306
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ipv4HostPortRegexp = regexp.MustCompile("^([^:]+):([0-9]+)$")
|
||||||
|
ipv4HostRegexp = regexp.MustCompile("^([^:]+)$")
|
||||||
|
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
|
// InstanceKey is an instance indicator, identified by hostname and port
|
||||||
type InstanceKey struct {
|
type InstanceKey struct {
|
||||||
Hostname string
|
Hostname string
|
||||||
@ -25,25 +33,35 @@ const detachHint = "//"
|
|||||||
|
|
||||||
// ParseInstanceKey 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
|
||||||
func NewRawInstanceKey(hostPort string) (*InstanceKey, error) {
|
func NewRawInstanceKey(hostPort string) (*InstanceKey, error) {
|
||||||
tokens := strings.SplitN(hostPort, ":", 2)
|
hostname := ""
|
||||||
if len(tokens) != 2 {
|
port := ""
|
||||||
return nil, fmt.Errorf("Cannot parse InstanceKey from %s. Expected format is host:port", hostPort)
|
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 if submatch := ipv6HostRegexp.FindStringSubmatch(hostPort); len(submatch) > 0 {
|
||||||
|
hostname = submatch[1]
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("Cannot parse address: %s", hostPort)
|
||||||
}
|
}
|
||||||
instanceKey := &InstanceKey{Hostname: tokens[0]}
|
instanceKey := &InstanceKey{Hostname: hostname, Port: DefaultInstancePort}
|
||||||
var err error
|
if port != "" {
|
||||||
if instanceKey.Port, err = strconv.Atoi(tokens[1]); err != nil {
|
var err error
|
||||||
return instanceKey, fmt.Errorf("Invalid port: %s", tokens[1])
|
if instanceKey.Port, err = strconv.Atoi(port); err != nil {
|
||||||
|
return instanceKey, fmt.Errorf("Invalid port: %s", port)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return instanceKey, nil
|
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
|
// The port part is optional; there will be no name resolve
|
||||||
func ParseRawInstanceKeyLoose(hostPort string) (*InstanceKey, error) {
|
func ParseInstanceKey(hostPort string) (*InstanceKey, error) {
|
||||||
if !strings.Contains(hostPort, ":") {
|
|
||||||
return &InstanceKey{Hostname: hostPort, Port: DefaultInstancePort}, nil
|
|
||||||
}
|
|
||||||
return NewRawInstanceKey(hostPort)
|
return NewRawInstanceKey(hostPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +92,7 @@ func (this *InstanceKeyMap) ReadCommaDelimitedList(list string) error {
|
|||||||
}
|
}
|
||||||
tokens := strings.Split(list, ",")
|
tokens := strings.Split(list, ",")
|
||||||
for _, token := range tokens {
|
for _, token := range tokens {
|
||||||
key, err := ParseRawInstanceKeyLoose(token)
|
key, err := ParseInstanceKey(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
74
go/mysql/instance_key_test.go
Normal file
74
go/mysql/instance_key_test.go
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
_, err := ParseInstanceKey("10.0.0.4:5.6.7")
|
||||||
|
test.S(t).ExpectNotNil(err)
|
||||||
|
}
|
||||||
|
}
|
@ -30,8 +30,6 @@ cp ${tarball}.gz "$BUILD_ARTIFACT_DIR"/gh-ost/
|
|||||||
|
|
||||||
### HACK HACK HACK HACK ###
|
### HACK HACK HACK HACK ###
|
||||||
# blame @carlosmn, @mattr and @timvaillancourt-
|
# blame @carlosmn, @mattr and @timvaillancourt-
|
||||||
# Allow builds on buster to also be used for stretch + jessie
|
# Allow builds on buster to also be used for stretch
|
||||||
stretch_tarball_name=$(echo $(basename "${tarball}") | sed s/-buster-/-stretch-/)
|
stretch_tarball_name=$(echo $(basename "${tarball}") | sed s/-buster-/-stretch-/)
|
||||||
jessie_tarball_name=$(echo $(basename "${stretch_tarball_name}") | sed s/-stretch-/-jessie-/)
|
|
||||||
cp ${tarball}.gz "$BUILD_ARTIFACT_DIR/gh-ost/${stretch_tarball_name}.gz"
|
cp ${tarball}.gz "$BUILD_ARTIFACT_DIR/gh-ost/${stretch_tarball_name}.gz"
|
||||||
cp ${tarball}.gz "$BUILD_ARTIFACT_DIR/gh-ost/${jessie_tarball_name}.gz"
|
|
||||||
|
Loading…
Reference in New Issue
Block a user