WIP
This commit is contained in:
parent
ad19d1e9c3
commit
115fcfb9e8
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,6 +1,6 @@
|
|||||||
/.gopath/
|
/.gopath/
|
||||||
/bin/
|
/bin/
|
||||||
/libexec/
|
/libexec/
|
||||||
/localtests/mysql.env
|
/localtests/tests.env
|
||||||
/.vendor/
|
/.vendor/
|
||||||
.idea/
|
.idea/
|
||||||
|
@ -8,6 +8,10 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/openark/golib/sqlutils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test represents a single test.
|
// Test represents a single test.
|
||||||
@ -133,11 +137,7 @@ func (test *Test) Migrate(config Config, primary, replica *sql.DB) (err error) {
|
|||||||
fmt.Printf("::group::%s gh-ost output\n", test.Name)
|
fmt.Printf("::group::%s gh-ost output\n", test.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = cmd.Start(); err != nil {
|
err = cmd.Run()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cmd.Wait()
|
|
||||||
|
|
||||||
if isGitHubActions() {
|
if isGitHubActions() {
|
||||||
fmt.Println("::endgroup::")
|
fmt.Println("::endgroup::")
|
||||||
@ -145,6 +145,7 @@ func (test *Test) Migrate(config Config, primary, replica *sql.DB) (err error) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if isExpectedFailureOutput(&stderr, test.ExpectedFailure) {
|
if isExpectedFailureOutput(&stderr, test.ExpectedFailure) {
|
||||||
|
log.Printf("[%s] test got expected failure: %s", test.ExpectedFailure)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
log.Printf("[%s] test failed: %+v", test.Name, stderr.String())
|
log.Printf("[%s] test failed: %+v", test.Name, stderr.String())
|
||||||
@ -152,61 +153,61 @@ func (test *Test) Migrate(config Config, primary, replica *sql.DB) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
func getTablePrimaryKey(db *sql.DB, database, table string) (string, error) {
|
||||||
func getPrimaryOrUniqueKey(db *sql.DB, database, table string) (string, error) {
|
|
||||||
return "id", nil // TODO: fix this
|
return "id", nil // TODO: fix this
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
type validationResult struct {
|
||||||
|
Source string
|
||||||
|
Rows sqlutils.RowMap
|
||||||
|
}
|
||||||
|
|
||||||
// Validate performs a validation of the migration test results.
|
// Validate performs a validation of the migration test results.
|
||||||
func (test *Test) Validate(config Config, primary, replica *sql.DB) error {
|
func (test *Test) Validate(config Config, db *sql.DB) error {
|
||||||
if len(test.ValidateColumns) == 0 || len(test.ValidateOrigColumns) == 0 {
|
if len(test.ValidateColumns) == 0 || len(test.ValidateOrigColumns) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
primaryKey, err := getTablePrimaryKey(db, testDatabase, testTable)
|
||||||
primaryKey, err := getPrimaryOrUniqueKey(replica, testDatabase, testTable)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var query string
|
limit := 10
|
||||||
var maxPrimaryKeyVal interface{}
|
orderBy := primaryKey
|
||||||
if maxPrimaryKeyVal == nil {
|
if test.ValidateOrderBy != "" {
|
||||||
query = fmt.Sprintf("select * from %s.%s limit 10", testDatabase, testTable)
|
orderBy = test.ValidateOrderBy
|
||||||
} else {
|
}
|
||||||
query = fmt.Sprintf("select * from %s.%s where %s > %+v limit 10",
|
|
||||||
testDatabase, testTable, primaryKey, maxPrimaryKeyVal,
|
outChan := make(chan validationResult, 2)
|
||||||
|
getTableMap := func(wg *sync.WaitGroup, database, table string, columns []string, outChan chan validationResult) {
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
|
query := fmt.Sprintf("select %s from %s.%s order by %s limit %d",
|
||||||
|
strings.Join(columns, ", "),
|
||||||
|
database, table,
|
||||||
|
orderBy, limit,
|
||||||
)
|
)
|
||||||
|
err := sqlutils.QueryRowsMap(db, query, func(m sqlutils.RowMap) error {
|
||||||
|
outChan <- validationResult{
|
||||||
|
Source: table,
|
||||||
|
Rows: m,
|
||||||
}
|
}
|
||||||
var rowMap sqlutils.RowMap
|
return nil
|
||||||
err = sqlutils.QueryRowsMap(replica, query, func(m sqlutils.RowMap) error {
|
|
||||||
for _, col := range test.ValidateColumns {
|
|
||||||
if val, found := m[col]; found {
|
|
||||||
rowMap[col] = val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[%s] failed to validate table %s: %+v", test.Name, table, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
values := make([]interface{}, 0)
|
var wg sync.WaitGroup
|
||||||
for range test.ValidateOrigColumns {
|
go getTableMap(&wg, testDatabase, testTable, test.ValidateColumns, outChan)
|
||||||
var val interface{}
|
go getTableMap(&wg, testDatabase, fmt.Sprintf("_%s_del", testTable), test.ValidateOrigColumns, outChan)
|
||||||
values = append(values, &val)
|
wg.Add(2)
|
||||||
}
|
wg.Wait()
|
||||||
maxPrimaryKeyVal = values[0]
|
|
||||||
|
|
||||||
for rows.Next() {
|
for result := range outChan {
|
||||||
if err = rows.Scan(values...); err != nil {
|
log.Printf("[%s] result for %s: %+v", test.Name, result.Source, result.Rows)
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
for i, value := range values {
|
|
||||||
if value == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Printf("[%s] row value for %q col: %d", test.Name, test.ValidateOrigColumns[i], value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,8 @@ func (t *Tester) ReadTests(specificTestName string) (tests []Test, err error) {
|
|||||||
test := Test{
|
test := Test{
|
||||||
Name: subdir.Name(),
|
Name: subdir.Name(),
|
||||||
Path: filepath.Join(t.config.TestsDir, subdir.Name()),
|
Path: filepath.Join(t.config.TestsDir, subdir.Name()),
|
||||||
|
ValidateColumns: []string{"*"},
|
||||||
|
ValidateOrigColumns: []string{"*"},
|
||||||
}
|
}
|
||||||
|
|
||||||
stat, err := os.Stat(test.Path)
|
stat, err := os.Stat(test.Path)
|
||||||
@ -147,7 +149,9 @@ func (t *Tester) ReadTests(specificTestName string) (tests []Test, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
orderByFile := filepath.Join(test.Path, "order_by")
|
orderByFile := filepath.Join(test.Path, "order_by")
|
||||||
test.ValidateOrderBy, _ = readTestFile(orderByFile)
|
if validateOrderBy, err := readTestFile(orderByFile); err == nil {
|
||||||
|
test.ValidateOrderBy = validateOrderBy
|
||||||
|
}
|
||||||
|
|
||||||
origColumnsFile := filepath.Join(test.Path, "orig_columns")
|
origColumnsFile := filepath.Join(test.Path, "orig_columns")
|
||||||
if origColumns, err := readTestFile(origColumnsFile); err == nil {
|
if origColumns, err := readTestFile(origColumnsFile); err == nil {
|
||||||
@ -194,7 +198,7 @@ func (t *Tester) RunTest(test Test) (err error) {
|
|||||||
}
|
}
|
||||||
log.Printf("[%s] successfully migrated test %s", test.Name, successEmoji)
|
log.Printf("[%s] successfully migrated test %s", test.Name, successEmoji)
|
||||||
|
|
||||||
if err = test.Validate(t.config, t.primary, t.replica); err != nil {
|
if err = test.Validate(t.config, t.replica); err != nil {
|
||||||
log.Printf("[%s] failed to validate test %s%s%s", test.Name, failedEmoji,
|
log.Printf("[%s] failed to validate test %s%s%s", test.Name, failedEmoji,
|
||||||
failedEmoji, failedEmoji)
|
failedEmoji, failedEmoji)
|
||||||
return err
|
return err
|
||||||
|
@ -5,7 +5,7 @@ COPY . /go/src/github.com/github/gh-ost
|
|||||||
WORKDIR /go/src/github.com/github/gh-ost
|
WORKDIR /go/src/github.com/github/gh-ost
|
||||||
|
|
||||||
RUN go build -o gh-ost go/cmd/gh-ost/main.go
|
RUN go build -o gh-ost go/cmd/gh-ost/main.go
|
||||||
RUN go build -o gh-ost-localtests go/cmd/gh-ost-localtests/main.go
|
RUN go build -o gh-ost-tester go/cmd/gh-ost-tester/main.go
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ RUN apt-get install -y default-mysql-client
|
|||||||
RUN rm -rf /var/lib/apt/lists/*
|
RUN rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY --from=build /go/src/github.com/github/gh-ost/gh-ost /usr/local/bin/gh-ost
|
COPY --from=build /go/src/github.com/github/gh-ost/gh-ost /usr/local/bin/gh-ost
|
||||||
COPY --from=build /go/src/github.com/github/gh-ost/gh-ost-localtests /usr/local/bin/gh-ost-localtests
|
COPY --from=build /go/src/github.com/github/gh-ost/gh-ost-tester /usr/local/bin/gh-ost-tester
|
||||||
COPY --from=build /go/src/github.com/github/gh-ost/localtests /etc/localtests
|
COPY --from=build /go/src/github.com/github/gh-ost/localtests /etc/localtests
|
||||||
|
|
||||||
ENTRYPOINT ["gh-ost-localtests"]
|
ENTRYPOINT ["gh-ost-tester"]
|
||||||
|
@ -4,22 +4,24 @@ services:
|
|||||||
build:
|
build:
|
||||||
context: "../"
|
context: "../"
|
||||||
dockerfile: "localtests/Dockerfile"
|
dockerfile: "localtests/Dockerfile"
|
||||||
env_file: "mysql.env"
|
env_file: "tests.env"
|
||||||
depends_on:
|
depends_on:
|
||||||
- "primary"
|
- "primary"
|
||||||
- "replica"
|
- "replica"
|
||||||
primary:
|
primary:
|
||||||
image: ${TEST_DOCKER_IMAGE}
|
image: ${TEST_DOCKER_IMAGE}
|
||||||
command: "--bind-address=0.0.0.0 --enforce-gtid-consistency --gtid-mode=ON --event-scheduler=ON --log-bin --log-slave-updates --server-id=1"
|
command: "--bind-address=0.0.0.0 --enforce-gtid-consistency --gtid-mode=ON --event-scheduler=ON --log-bin --log-slave-updates --server-id=1"
|
||||||
env_file: "mysql.env"
|
env_file: "tests.env"
|
||||||
volumes:
|
volumes:
|
||||||
- "./init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro"
|
- "./init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro"
|
||||||
replica:
|
replica:
|
||||||
image: ${TEST_DOCKER_IMAGE}
|
image: ${TEST_DOCKER_IMAGE}
|
||||||
command: "--bind-address=0.0.0.0 --enforce-gtid-consistency --gtid-mode=ON --log-bin --log-slave-updates --read-only=ON --server-id=2"
|
command: "--bind-address=0.0.0.0 --enforce-gtid-consistency --gtid-mode=ON --log-bin --log-slave-updates --read-only=ON --server-id=2"
|
||||||
env_file: "mysql.env"
|
env_file: "tests.env"
|
||||||
depends_on:
|
depends_on:
|
||||||
- "primary"
|
- "primary"
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
volumes:
|
volumes:
|
||||||
- "./init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro"
|
- "./init.sql:/docker-entrypoint-initdb.d/01-init.sql:ro"
|
||||||
- "./init-replica.sql:/docker-entrypoint-initdb.d/02-init-replica.sql:ro"
|
- "./init-replica.sql:/docker-entrypoint-initdb.d/02-init-replica.sql:ro"
|
||||||
|
@ -9,29 +9,47 @@ if [ -z "$TEST_DOCKER_IMAGE" ]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# generate mysql.env file for containers
|
if [ -z "$TEST_STORAGE_ENGINE" ]; then
|
||||||
|
TEST_STORAGE_ENGINE=innodb
|
||||||
|
fi
|
||||||
|
|
||||||
|
# generate env file for containers
|
||||||
|
generate_test_env() {
|
||||||
[ ! -z "$GITHUB_ACTION" ] && echo "::group::generate mysql env"
|
[ ! -z "$GITHUB_ACTION" ] && echo "::group::generate mysql env"
|
||||||
|
|
||||||
(
|
(
|
||||||
echo "GITHUB_ACTION=${GITHUB_ACTION}"
|
echo "GITHUB_ACTION=${GITHUB_ACTION}"
|
||||||
echo 'MYSQL_ALLOW_EMPTY_PASSWORD=true'
|
echo 'MYSQL_ALLOW_EMPTY_PASSWORD=true'
|
||||||
echo "TEST_STORAGE_ENGINE=${TEST_STORAGE_ENGINE}"
|
echo "TEST_STORAGE_ENGINE=${TEST_STORAGE_ENGINE}"
|
||||||
[ "$TEST_STORAGE_ENGINE" == "rocksdb" ] && echo 'INIT_ROCKSDB=true'
|
[ "$TEST_STORAGE_ENGINE" == "rocksdb" ] && echo 'INIT_ROCKSDB=true'
|
||||||
) | tee $LOCALTESTS_DIR/mysql.env
|
) | tee $LOCALTESTS_DIR/tests.env
|
||||||
echo "Wrote env file to $LOCALTESTS_DIR/mysql.env"
|
echo "Wrote env file to $LOCALTESTS_DIR/tests.env"
|
||||||
|
|
||||||
[ ! -z "$GITHUB_ACTION" ] && echo "::endgroup::"
|
[ ! -z "$GITHUB_ACTION" ] && echo "::endgroup::"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# prebuild the test docker image
|
||||||
|
prebuild_docker_image() {
|
||||||
|
echo "::group::docker image build"
|
||||||
|
docker-compose -f localtests/docker-compose.yml build
|
||||||
|
echo "::endgroup::"
|
||||||
|
}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
generate_test_env
|
||||||
|
|
||||||
# conditional pre-build
|
# conditional pre-build
|
||||||
EXTRA_UP_FLAGS=
|
EXTRA_UP_FLAGS=
|
||||||
if [ -z "$GITHUB_ACTION" ]; then
|
if [ -z "$GITHUB_ACTION" ]; then
|
||||||
EXTRA_UP_FLAGS="--build"
|
EXTRA_UP_FLAGS="--build"
|
||||||
else
|
else
|
||||||
echo "::group::docker image build"
|
prebuild_docker_image
|
||||||
docker-compose -f localtests/docker-compose.yml build
|
|
||||||
echo "::endgroup::"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# this will start the test container and the
|
# start test container and mysql primary/replica
|
||||||
# mysql primary/replica w/docker-compose
|
# with docker-compose
|
||||||
docker-compose -f localtests/docker-compose.yml up \
|
docker-compose -f localtests/docker-compose.yml up \
|
||||||
--abort-on-container-exit \
|
--abort-on-container-exit \
|
||||||
--no-log-prefix \
|
--no-log-prefix \
|
||||||
|
Loading…
Reference in New Issue
Block a user