2021-07-08 15:50:43 +00:00
|
|
|
package mysql
|
|
|
|
|
|
|
|
import (
|
|
|
|
"database/sql"
|
|
|
|
"fmt"
|
2021-07-16 16:13:51 +00:00
|
|
|
"os"
|
|
|
|
"os/exec"
|
2021-08-04 13:40:04 +00:00
|
|
|
"runtime"
|
|
|
|
"strings"
|
2021-07-16 16:13:51 +00:00
|
|
|
"testing"
|
|
|
|
|
2021-07-08 15:50:43 +00:00
|
|
|
"github.com/WatchBeam/clock"
|
|
|
|
"github.com/fleetdm/fleet/v4/server/config"
|
|
|
|
"github.com/go-kit/kit/log"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
schemaDbName = "schemadb"
|
2021-07-19 21:20:31 +00:00
|
|
|
dumpfile = "/tmpfs/dump.sql"
|
2021-07-08 15:50:43 +00:00
|
|
|
testUsername = "root"
|
|
|
|
testPassword = "toor"
|
|
|
|
testAddress = "localhost:3307"
|
|
|
|
)
|
|
|
|
|
2021-07-16 16:13:51 +00:00
|
|
|
func panicif(err error) {
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
2021-07-08 15:50:43 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 16:13:51 +00:00
|
|
|
// initializeSchemaOrPanic initializes a database schema using the normal Fleet
|
2021-07-08 15:50:43 +00:00
|
|
|
// migrations, then outputs the schema with mysqldump within the MySQL Docker
|
|
|
|
// container.
|
2021-07-16 16:13:51 +00:00
|
|
|
func initializeSchemaOrPanic() {
|
2021-07-08 15:50:43 +00:00
|
|
|
// Create the database (must use raw MySQL client to do this)
|
|
|
|
db, err := sql.Open(
|
|
|
|
"mysql",
|
|
|
|
fmt.Sprintf("%s:%s@tcp(%s)/?multiStatements=true", testUsername, testPassword, testAddress),
|
|
|
|
)
|
2021-07-16 16:13:51 +00:00
|
|
|
panicif(err)
|
2021-07-08 15:50:43 +00:00
|
|
|
defer db.Close()
|
|
|
|
_, err = db.Exec("DROP DATABASE IF EXISTS schemadb; CREATE DATABASE schemadb;")
|
2021-07-16 16:13:51 +00:00
|
|
|
panicif(err)
|
2021-07-08 15:50:43 +00:00
|
|
|
|
|
|
|
// Create a datastore client in order to run migrations as usual
|
|
|
|
config := config.MysqlConfig{
|
|
|
|
Username: testUsername,
|
|
|
|
Password: testPassword,
|
|
|
|
Address: testAddress,
|
|
|
|
Database: schemaDbName,
|
|
|
|
}
|
|
|
|
ds, err := New(config, clock.NewMockClock(), Logger(log.NewNopLogger()), LimitAttempts(1))
|
2021-07-16 16:13:51 +00:00
|
|
|
panicif(err)
|
2021-07-08 15:50:43 +00:00
|
|
|
defer ds.Close()
|
2021-07-16 16:13:51 +00:00
|
|
|
panicif(ds.MigrateTables())
|
2021-07-08 15:50:43 +00:00
|
|
|
|
|
|
|
// Dump schema to dumpfile
|
|
|
|
if out, err := exec.Command(
|
|
|
|
"docker-compose", "exec", "-T", "mysql_test",
|
|
|
|
// Command run inside container
|
|
|
|
"mysqldump",
|
|
|
|
"-u"+testUsername, "-p"+testPassword,
|
|
|
|
"schemadb",
|
|
|
|
"--compact", "--skip-comments",
|
|
|
|
"--result-file="+dumpfile,
|
|
|
|
).CombinedOutput(); err != nil {
|
2021-07-16 16:13:51 +00:00
|
|
|
fmt.Println(string(out))
|
|
|
|
panicif(err)
|
2021-07-08 15:50:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-16 16:13:51 +00:00
|
|
|
func connectMySQL(t *testing.T, testName string) *Datastore {
|
|
|
|
config := config.MysqlConfig{
|
|
|
|
Username: testUsername,
|
|
|
|
Password: testPassword,
|
|
|
|
Database: testName,
|
|
|
|
Address: testAddress,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create datastore client
|
|
|
|
ds, err := New(config, clock.NewMockClock(), Logger(log.NewNopLogger()), LimitAttempts(1))
|
|
|
|
require.Nil(t, err)
|
|
|
|
return ds
|
|
|
|
}
|
|
|
|
|
2021-07-08 15:50:43 +00:00
|
|
|
// initializeDatabase loads the dumped schema into a newly created database in
|
|
|
|
// MySQL. This is much faster than running the full set of migrations on each
|
|
|
|
// test.
|
2021-07-16 16:13:51 +00:00
|
|
|
func initializeDatabase(t *testing.T, testName string) *Datastore {
|
2021-07-08 15:50:43 +00:00
|
|
|
// Load schema from dumpfile
|
|
|
|
if out, err := exec.Command(
|
|
|
|
"docker-compose", "exec", "-T", "mysql_test",
|
|
|
|
// Command run inside container
|
|
|
|
"mysql",
|
|
|
|
"-u"+testUsername, "-p"+testPassword,
|
|
|
|
"-e",
|
|
|
|
fmt.Sprintf(
|
|
|
|
"DROP DATABASE IF EXISTS %s; CREATE DATABASE %s; USE %s; SET FOREIGN_KEY_CHECKS=0; SOURCE %s;",
|
2021-07-16 16:13:51 +00:00
|
|
|
testName, testName, testName, dumpfile,
|
2021-07-08 15:50:43 +00:00
|
|
|
),
|
|
|
|
).CombinedOutput(); err != nil {
|
|
|
|
t.Error(err)
|
|
|
|
t.Error(string(out))
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2021-07-16 16:13:51 +00:00
|
|
|
return connectMySQL(t, testName)
|
2021-07-08 15:50:43 +00:00
|
|
|
}
|
|
|
|
|
2021-07-16 16:13:51 +00:00
|
|
|
func CreateMySQLDS(t *testing.T) *Datastore {
|
|
|
|
if _, ok := os.LookupEnv("MYSQL_TEST"); !ok {
|
|
|
|
t.Skip("MySQL tests are disabled")
|
|
|
|
}
|
|
|
|
|
|
|
|
t.Parallel()
|
|
|
|
|
2021-08-04 13:40:04 +00:00
|
|
|
pc, _, _, ok := runtime.Caller(1)
|
|
|
|
details := runtime.FuncForPC(pc)
|
|
|
|
if !ok || details == nil {
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanName := strings.ReplaceAll(
|
|
|
|
strings.TrimPrefix(details.Name(), "github.com/fleetdm/fleet/v4/"), "/", "_",
|
|
|
|
)
|
|
|
|
cleanName = strings.ReplaceAll(cleanName, ".", "_")
|
|
|
|
return initializeDatabase(t, cleanName)
|
2021-07-16 16:13:51 +00:00
|
|
|
}
|