"unexpected EOF" error using golang-migrate and cloud sql proxy on github actions

120 views Asked by At

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?

1

There are 1 answers

1
Łukasz D. Tulikowski On

To resolve the issue, try adjusting the second parameter by setting it to "mysql" as the driver name.

if err != nil {
        panic(err)
    }
    // Changed the second parameter to the driver name to mysql here 
    m, err := migrate.NewWithDatabaseInstance(source, "mysql", driver) 
if err != nil {
   panic(err)
}