This is something I've had no problem doing with Postgres, but I just can't get it to work with MySQL.
I have this step in my Github Actions YML, which previously checks out my repo code and installs Go 1.21:
- name: Run database migrations
run: |
make proxy_db
make migration
The proxy_db makefile command is defined as:
proxy_db:
docker run -d \
-p 127.0.0.1:3306:3306 \
-v $(GOOGLE_APPLICATION_CREDENTIALS):/config \
gcr.io/cloudsql-docker/gce-proxy:1.16 /cloud_sql_proxy \
-instances=$(CLOUD_SQL_INSTANCE_NAME)=tcp:0.0.0.0:3306 -credential_file=/config
The make migration makefile command is defined as:
migration:
@go run cmd/migration/main.go
The contents of main.go are as follows:
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/GoogleCloudPlatform/berglas/pkg/auto"
"github.com/golang-migrate/migrate/v4"
"github.com/golang-migrate/migrate/v4/database/mysql"
_ "github.com/golang-migrate/migrate/v4/database/mysql"
_ "github.com/golang-migrate/migrate/v4/source/file"
"github.com/kelseyhightower/envconfig"
)
type variables struct {
MySQLProxyPort int `required:"false" envconfig:"mysql_proxy_port"`
MySQLDB string `required:"true" envconfig:"mysql_db"`
MySQLUser string `required:"true" envconfig:"mysql_user"`
MySQLPass string `required:"true" envconfig:"mysql_pass"`
Env string `envconfig:"build_env"`
}
const migrationsPath = "scripts/db/migrations"
func main() {
var v variables
envconfig.MustProcess("db migration", &v)
db, err := sql.Open("mysql", getDbSource(v))
db.SetConnMaxLifetime(time.Minute * 5)
source := fmt.Sprintf("file://%s", migrationsPath)
driver, err := mysql.WithInstance(db, &mysql.Config{DatabaseName: v.MySQLDB, StatementTimeout: 300 * time.Second})
if err != nil {
panic(err)
}
m, err := migrate.NewWithDatabaseInstance(source, v.MySQLDB, driver)
if err != nil {
panic(err)
}
fmt.Println("Running migrations...")
if err := m.Up(); err != nil {
if err == migrate.ErrNoChange {
fmt.Println("No migrations to run.")
return
}
panic(err)
}
fmt.Println("Migrations ran successfully.")
}
func getDbSource(v variables) string {
tls := fmt.Sprintf("?tls=%t", v.Env == "live")
return fmt.Sprintf(
"%s:%s@tcp(localhost:%v)/%s%s",
v.MySQLUser,
v.MySQLPass,
v.MySQLProxyPort,
v.MySQLDB,
tls,
)
}
func getDbUrl(v variables) string {
return fmt.Sprintf(
"mysql://%s", getDbSource(v),
)
}
My env vars appear to be loading correctly, including those which contain references to Cloud Secrets, and the cloud SQL proxy starts up without issue, so I don't believe there is a problem with my GOOGLE_APPLICATION_CREDENTIALS. My migrationsPath variable in main.go doesn't contain any typos.
After about a minute, the Github Action exits with these error logs:
[mysql] 2024/02/19 01:47:19 packets.go:37: unexpected EOF
[mysql] 2024/02/19 01:47:19 packets.go:37: unexpected EOF
[mysql] 2024/02/19 01:47:19 packets.go:37: unexpected EOF
panic: driver: bad connection
goroutine 1 [running]:
main.main()
/home/.../cmd/migration/main.go:35 +0x314
exit status 2
make: *** [makefile:14: migration] Error 1
Error: Process completed with exit code 2.
I've also tried this with a wait script between the two commands of my GHA step, but that didn't make a difference, the DB is ready after 0 seconds.
What is wrong with my steps?
To resolve the issue, try adjusting the second parameter by setting it to
"mysql"as the driver name.