Skip to content

Commit d9ce3ea

Browse files
Golang localtests
1 parent 9b3fa79 commit d9ce3ea

File tree

18 files changed

+1072
-19
lines changed

18 files changed

+1072
-19
lines changed

Dockerfile.ripple

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
FROM ubuntu:bionic AS build
2+
3+
RUN apt-get update && apt-get install -y gnupg2 wget curl lsb-release git pkg-config \
4+
zip g++ zlib1g-dev unzip python libssl-dev default-jdk-headless libmariadbclient-dev
5+
6+
RUN echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | tee /etc/apt/sources.list.d/bazel.list
7+
RUN curl https://bazel.build/bazel-release.pub.gpg | apt-key add -
8+
RUN apt-get update && apt-get install -y bazel
9+
10+
RUN git clone https://github.com/google/mysql-ripple.git && \
11+
cd mysql-ripple && \
12+
bazel build :all && \
13+
bazel test :all
14+
15+
FROM ubuntu:bionic
16+
17+
RUN apt-get update && apt-get install -y libssl-dev
18+
19+
COPY --from=build /mysql-ripple/bazel-bin/rippled /usr/local/bin/rippled
20+
21+
ENTRYPOINT ["rippled"]

go.mod

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@ require (
66
github.com/go-ini/ini v1.62.0
77
github.com/go-mysql-org/go-mysql v1.3.0
88
github.com/go-sql-driver/mysql v1.6.0
9+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
910
github.com/openark/golib v0.0.0-20210531070646-355f37940af8
1011
github.com/satori/go.uuid v1.2.0
11-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83
1212
golang.org/x/net v0.0.0-20210224082022-3d97a244fca7
13+
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467
1314
golang.org/x/text v0.3.6
1415
)
1516

@@ -21,7 +22,6 @@ require (
2122
github.com/smartystreets/goconvey v1.6.4 // indirect
2223
go.uber.org/atomic v1.7.0 // indirect
2324
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect
24-
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 // indirect
2525
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
2626
gopkg.in/ini.v1 v1.62.0 // indirect
2727
)

go.sum

+2-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
1717
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
1818
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
1919
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
20+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
21+
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
2022
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
2123
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
2224
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
@@ -81,8 +83,6 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
8183
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
8284
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
8385
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
84-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g=
85-
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
8686
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
8787
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
8888
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -96,14 +96,10 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
9696
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
9797
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
9898
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
99-
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
10099
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
101-
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
102100
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
103101
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4=
104102
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
105-
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
106-
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
107103
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
108104
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467 h1:CBpWXWQpIRjzmkkA+M7q9Fqnwd2mZr3AFqexg8YTfoM=
109105
golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=

go/cmd/gh-ost-localtests/main.go

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package main
2+
3+
import (
4+
"database/sql"
5+
"flag"
6+
"fmt"
7+
"log"
8+
"os"
9+
"path/filepath"
10+
11+
_ "github.com/go-sql-driver/mysql"
12+
13+
"github.com/github/gh-ost/go/localtests"
14+
)
15+
16+
var AppVersion string
17+
18+
func main() {
19+
pwd, err := os.Getwd()
20+
if err != nil {
21+
panic(err)
22+
}
23+
24+
var printVersion, testNoop bool
25+
var localtestsDir, testName string
26+
var cnf localtests.Config
27+
28+
flag.StringVar(&cnf.Host, "host", localtests.DefaultHost, "mysql host")
29+
flag.Int64Var(&cnf.Port, "port", localtests.DefaultPort, "mysql port")
30+
flag.StringVar(&cnf.Username, "username", localtests.DefaultUsername, "mysql username")
31+
flag.StringVar(&cnf.Password, "password", localtests.DefaultPassword, "mysql password")
32+
flag.StringVar(&localtestsDir, "tests-dir", filepath.Join(pwd, "localtests"), "path to localtests directory")
33+
flag.StringVar(&testName, "test", "", "run a single test by name (default: run all tests)")
34+
flag.BoolVar(&testNoop, "test-noop", false, "run a single noop migration, eg: --alter='ENGINE=InnoDB'")
35+
flag.StringVar(&cnf.GhostBinary, "binary", "gh-ost", "path to gh-ost binary")
36+
flag.StringVar(&cnf.MysqlBinary, "mysql-binary", "mysql", "path to mysql binary")
37+
flag.BoolVar(&printVersion, "version", false, "print version and exit")
38+
flag.Parse()
39+
40+
if printVersion {
41+
fmt.Println(AppVersion)
42+
os.Exit(0)
43+
}
44+
45+
db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%d)/?interpolateParams=true",
46+
cnf.Username,
47+
cnf.Password,
48+
cnf.Host,
49+
cnf.Port,
50+
))
51+
if err != nil {
52+
log.Fatal(err)
53+
}
54+
defer db.Close()
55+
56+
if err = localtests.WaitForMySQLAvailable(db); err != nil {
57+
log.Fatalf("Failed to setup database client: %+v", err)
58+
}
59+
60+
var tests []localtests.Test
61+
if testNoop {
62+
tests = []localtests.Test{
63+
{
64+
Name: "noop",
65+
ExtraArgs: []string{
66+
`--alter='ENGINE=InnoDB'`,
67+
},
68+
},
69+
}
70+
} else {
71+
tests, err = localtests.ReadTests(localtestsDir)
72+
if err != nil {
73+
log.Fatalf("Failed to read tests: %+v", err)
74+
}
75+
}
76+
77+
for _, test := range tests {
78+
if testName != "" && test.Name != testName {
79+
continue
80+
}
81+
82+
log.Printf("Found test %q at %s/%s", test.Name, localtestsDir, test.Name)
83+
if err = localtests.RunTest(db, cnf, test); err != nil {
84+
log.Fatalf("Failed to run test %s: %+v", test.Name, err)
85+
}
86+
}
87+
}

go/localtests/localtests.go

+202
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package localtests
2+
3+
import (
4+
"database/sql"
5+
"errors"
6+
"fmt"
7+
"io/ioutil"
8+
"log"
9+
"os"
10+
"os/exec"
11+
"path/filepath"
12+
"strings"
13+
"time"
14+
15+
"github.com/google/shlex"
16+
)
17+
18+
const (
19+
PrimaryHost = "primary"
20+
DefaultHost = "replica"
21+
DefaultPort int64 = 3306
22+
DefaultUsername = "gh-ost"
23+
DefaultPassword = "gh-ost"
24+
testDatabase = "test"
25+
testTable = "gh_ost_test"
26+
testSocketFile = "/tmp/gh-ost.test.sock"
27+
throttleFlagFile = "/tmp/gh-ost-test.ghost.throttle.flag"
28+
throttleQuery = "select timestampdiff(second, min(last_update), now()) < 5 from _gh_ost_test_ghc"
29+
)
30+
31+
type Config struct {
32+
Host string
33+
Port int64
34+
Username string
35+
Password string
36+
GhostBinary string
37+
MysqlBinary string
38+
}
39+
40+
type Test struct {
41+
Name string
42+
Path string
43+
CreateSQLFile string
44+
ExtraArgs []string
45+
IgnoreVersions []string
46+
}
47+
48+
func WaitForMySQLAvailable(db *sql.DB) error {
49+
ticker := time.NewTicker(time.Second)
50+
defer ticker.Stop()
51+
52+
for {
53+
select {
54+
case <-time.After(10 * time.Minute):
55+
return errors.New("timed out waiting for mysql")
56+
case <-ticker.C:
57+
if err := db.Ping(); err != nil {
58+
log.Println("Waiting for mysql to become available")
59+
} else {
60+
log.Println("MySQL is available")
61+
return nil
62+
}
63+
}
64+
}
65+
}
66+
67+
// Prepare runs a 'mysql' client/shell command to populate the test schema.
68+
// The create.sql file is read by golang and passed to 'mysql' over stdin.
69+
func (test *Test) Prepare(config Config) error {
70+
if test.CreateSQLFile == "" {
71+
return nil
72+
}
73+
74+
defaultsFile, err := writeMysqlClientDefaultsFile(config)
75+
if err != nil {
76+
return err
77+
}
78+
defer os.Remove(defaultsFile)
79+
80+
flags := []string{
81+
fmt.Sprintf("--defaults-file=%s", defaultsFile),
82+
fmt.Sprintf("--host=%s", PrimaryHost), // TODO: fix this
83+
fmt.Sprintf("--port=%d", config.Port),
84+
"--default-character-set=utf8mb4",
85+
testDatabase,
86+
}
87+
log.Printf("[%s] running command: %s\n %s", test.Name, config.MysqlBinary, strings.Join(flags, "\n "))
88+
89+
createSQL, err := os.Open(test.CreateSQLFile)
90+
if err != nil {
91+
return err
92+
}
93+
defer createSQL.Close()
94+
log.Printf("[%s] loaded sql from: %s", test.Name, test.CreateSQLFile)
95+
96+
cmd := exec.Command(config.MysqlBinary, flags...)
97+
cmd.Stdin = createSQL
98+
cmd.Stdout = os.Stdout
99+
cmd.Stderr = os.Stderr
100+
return cmd.Run()
101+
}
102+
103+
func (test *Test) Migrate(db *sql.DB, config Config) error {
104+
mysqlInfo, err := getMysqlHostInfo(db)
105+
if err != nil {
106+
return err
107+
}
108+
log.Printf("[%s] detected MySQL %s host %s:%d", test.Name, mysqlInfo.Version, config.Host, config.Port)
109+
110+
flags := []string{
111+
fmt.Sprintf("--user=%s", config.Username),
112+
fmt.Sprintf("--password=%s", config.Password),
113+
fmt.Sprintf("--host=%s", config.Host),
114+
fmt.Sprintf("--port=%d", config.Port),
115+
fmt.Sprintf("--assume-master-host=primary:%d", mysqlInfo.Port), // TODO: fix this
116+
fmt.Sprintf("--database=%s", testDatabase),
117+
fmt.Sprintf("--table=%s", testTable),
118+
"--assume-rbr",
119+
"--chunk-size=10",
120+
"--default-retries=3",
121+
"--exact-rowcount",
122+
"--initially-drop-old-table",
123+
"--initially-drop-ghost-table",
124+
"--initially-drop-socket-file",
125+
fmt.Sprintf("--throttle-query=%s", throttleQuery),
126+
fmt.Sprintf("--throttle-flag-file=%s", throttleFlagFile),
127+
fmt.Sprintf("--serve-socket-file=%s", testSocketFile),
128+
//"--test-on-replica",
129+
"--allow-on-master",
130+
"--debug",
131+
"--execute",
132+
"--stack",
133+
"--verbose",
134+
}
135+
if len(test.ExtraArgs) > 0 {
136+
flags = append(flags, test.ExtraArgs...)
137+
} else {
138+
flags = append(flags, `--alter='ENGINE=InnoDB'`)
139+
}
140+
141+
log.Printf("[%s] running gh-ost command: %s\n %s", test.Name, config.GhostBinary, strings.Join(flags, "\n "))
142+
cmd := exec.Command(config.GhostBinary, flags...)
143+
cmd.Stdout = os.Stdout
144+
cmd.Stderr = os.Stderr
145+
return cmd.Run()
146+
}
147+
148+
func ReadTests(testsDir string) (tests []Test, err error) {
149+
subdirs, err := ioutil.ReadDir(testsDir)
150+
if err != nil {
151+
return tests, err
152+
}
153+
154+
for _, subdir := range subdirs {
155+
test := Test{
156+
Name: subdir.Name(),
157+
Path: filepath.Join(testsDir, subdir.Name()),
158+
}
159+
160+
stat, err := os.Stat(test.Path)
161+
if err != nil || !stat.IsDir() {
162+
continue
163+
}
164+
165+
test.CreateSQLFile = filepath.Join(test.Path, "create.sql")
166+
if _, err = os.Stat(test.CreateSQLFile); err != nil {
167+
log.Printf("Failed to find create.sql file %q: %+v", test.CreateSQLFile, err)
168+
return tests, err
169+
}
170+
171+
extraArgsFile := filepath.Join(test.Path, "extra_args")
172+
if _, err = os.Stat(extraArgsFile); err == nil {
173+
extraArgsStr, err := readTestFile(extraArgsFile)
174+
if err != nil {
175+
log.Printf("Failed to read extra_args file %q: %+v", extraArgsFile, err)
176+
return tests, err
177+
}
178+
if test.ExtraArgs, err = shlex.Split(extraArgsStr); err != nil {
179+
log.Printf("Failed to read extra_args file %q: %+v", extraArgsFile, err)
180+
return tests, err
181+
}
182+
}
183+
184+
tests = append(tests, test)
185+
}
186+
187+
return tests, err
188+
}
189+
190+
func RunTest(db *sql.DB, config Config, test Test) error {
191+
if err := test.Prepare(config); err != nil {
192+
return err
193+
}
194+
log.Printf("[%s] prepared test", test.Name)
195+
196+
if err := test.Migrate(db, config); err != nil {
197+
return err
198+
}
199+
log.Printf("[%s] migrated test", test.Name)
200+
201+
return nil
202+
}

0 commit comments

Comments
 (0)