Skip to content

Commit fcf1ff1

Browse files
committedOct 15, 2019
Bundle migrations in binary
Use the go-bindata tool to generate a golang migrations file to compile the content of the db_migrations folder into the bundle. This ensures that the compiled binaries are portable and do not require around the migrations themselves along with the binary. Also updated the makefile to pull the go-bindata file and generate the `migration.go` file as part of `make build`
1 parent b9de978 commit fcf1ff1

File tree

13 files changed

+387
-26
lines changed

13 files changed

+387
-26
lines changed
 

‎Makefile

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ CMDS := $(addprefix bin/, $(shell go list $(MOD_FLAGS) ./cmd/... | xargs -I{} b
33

44
.PHONY: build test vendor clean
55

6-
all: clean test build
6+
all: clean install-go-bindata test build
77

88
$(CMDS):
99
go build $(MOD_FLAGS) $(extra_flags) -o $@ ./cmd/$(shell basename $@)
@@ -25,6 +25,12 @@ image-upstream:
2525
vendor:
2626
go mod vendor
2727

28+
install-go-bindata:
29+
go get -u github.com/go-bindata/go-bindata/...
30+
31+
generate-migration-bundle:
32+
go-bindata -pkg sqlite -o ./pkg/sqlite/migrations.go ./pkg/sqlite/db_migrations/
33+
2834
codegen:
2935
protoc -I pkg/api/ --go_out=plugins=grpc:pkg/api pkg/api/*.proto
3036
protoc -I pkg/api/grpc_health_v1 --go_out=plugins=grpc:pkg/api/grpc_health_v1 pkg/api/grpc_health_v1/*.proto

‎cmd/configmap-server/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func runCmdFunc(cmd *cobra.Command, args []string) error {
9898
logger.Fatalf("error getting configmap: %s", err)
9999
}
100100

101-
sqlLoader, err := sqlite.NewSQLLiteLoader(dbName, "")
101+
sqlLoader, err := sqlite.NewSQLLiteLoader(sqlite.WithDBName(dbName))
102102
if err != nil {
103103
return err
104104
}

‎cmd/initializer/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ func runCmdFunc(cmd *cobra.Command, args []string) error {
5353
return err
5454
}
5555

56-
dbLoader, err := sqlite.NewSQLLiteLoader(outFilename, "")
56+
dbLoader, err := sqlite.NewSQLLiteLoader(sqlite.WithDBName(outFilename))
5757
if err != nil {
5858
return err
5959
}

‎pkg/appregistry/dbloader.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
func NewDbLoader(dbName string, logger *logrus.Entry) (*dbLoader, error) {
14-
sqlLoader, err := sqlite.NewSQLLiteLoader(dbName, "")
14+
sqlLoader, err := sqlite.NewSQLLiteLoader(sqlite.WithDBName(dbName))
1515
if err != nil {
1616
return nil, err
1717
}

‎pkg/server/server_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func server() {
2929
}
3030
s := grpc.NewServer()
3131

32-
load, err := sqlite.NewSQLLiteLoader(dbName, "../sqlite/db_migrations")
32+
load, err := sqlite.NewSQLLiteLoader(sqlite.WithDBName(dbName))
3333
if err != nil {
3434
logrus.Fatal(err)
3535
}

‎pkg/sqlite/configmap_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
func TestConfigMapLoader(t *testing.T) {
1717
logrus.SetLevel(logrus.DebugLevel)
1818

19-
store, err := NewSQLLiteLoader("test.db", "./db_migrations")
19+
store, err := NewSQLLiteLoader(WithDBName("test.db"))
2020
require.NoError(t, err)
2121
defer os.Remove("test.db")
2222

@@ -34,7 +34,7 @@ func TestConfigMapLoader(t *testing.T) {
3434
}
3535

3636
func TestQuerierForConfigmap(t *testing.T) {
37-
load, err := NewSQLLiteLoader("test.db", "./db_migrations")
37+
load, err := NewSQLLiteLoader(WithDBName("test.db"))
3838
require.NoError(t, err)
3939
defer os.Remove("test.db")
4040

‎pkg/sqlite/db_options.go

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package sqlite
2+
3+
type DbOptions struct {
4+
// OutFileName is used to define the database file name that is generated from the loader
5+
OutFileName string
6+
7+
// Migrator refers to the SQL migrator used to initialize the database with
8+
MigrationsPath string
9+
}
10+
11+
type DbOption func(*DbOptions)
12+
13+
func WithDBName(name string) DbOption {
14+
return func(o *DbOptions) {
15+
o.OutFileName = name
16+
}
17+
}
18+
19+
func WithMigrationsPath(path string) DbOption {
20+
return func(o *DbOptions) {
21+
o.MigrationsPath = path
22+
}
23+
}

‎pkg/sqlite/directory_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
func TestDirectoryLoader(t *testing.T) {
1919
logrus.SetLevel(logrus.DebugLevel)
2020

21-
store, err := NewSQLLiteLoader("test.db", "./db_migrations")
21+
store, err := NewSQLLiteLoader(WithDBName("test.db"))
2222
require.NoError(t, err)
2323
defer func() {
2424
if err := os.Remove("test.db"); err != nil {
@@ -33,7 +33,7 @@ func TestDirectoryLoader(t *testing.T) {
3333
func TestDirectoryLoaderWithBadManifests(t *testing.T) {
3434
logrus.SetLevel(logrus.DebugLevel)
3535

36-
store, err := NewSQLLiteLoader("test.db", "./db_migrations")
36+
store, err := NewSQLLiteLoader(WithDBName("test.db"))
3737
require.NoError(t, err)
3838
defer func() {
3939
if err := os.Remove("test.db"); err != nil {
@@ -72,7 +72,7 @@ func TestDirectoryLoaderWithBadManifests(t *testing.T) {
7272
}
7373

7474
func TestQuerierForDirectory(t *testing.T) {
75-
load, err := NewSQLLiteLoader("test.db", "./db_migrations")
75+
load, err := NewSQLLiteLoader(WithDBName("test.db"))
7676
require.NoError(t, err)
7777
defer func() {
7878
if err := os.Remove("test.db"); err != nil {

‎pkg/sqlite/load.go

+13-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,13 @@ type SQLLoader struct {
2020

2121
var _ registry.Load = &SQLLoader{}
2222

23-
func NewSQLLiteLoader(outFilename, migrationsPath string) (*SQLLoader, error) {
24-
db, err := sql.Open("sqlite3", outFilename) // TODO: ?immutable=true
23+
func NewSQLLiteLoader(opts ...DbOption) (*SQLLoader, error) {
24+
options := DbOptions{}
25+
for _, o := range opts {
26+
o(&options)
27+
}
28+
29+
db, err := sql.Open("sqlite3", options.OutFileName) // TODO: ?immutable=true
2530
if err != nil {
2631
return nil, err
2732
}
@@ -79,7 +84,12 @@ func NewSQLLiteLoader(outFilename, migrationsPath string) (*SQLLoader, error) {
7984
}
8085

8186
// Apply the current latest database version to keep net new databases in sync with upgradeable ones
82-
migrator := NewSQLLiteMigrator(db, migrationsPath)
87+
migrator, err := NewSQLLiteMigrator(db, options.MigrationsPath)
88+
if err != nil {
89+
return nil, err
90+
}
91+
defer migrator.CleanUpMigrator()
92+
8393
err = migrator.InitMigrationVersion()
8494
if err != nil {
8595
return nil, err

‎pkg/sqlite/load_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func TestAddPackageChannels(t *testing.T) {
131131
for _, tt := range tests {
132132
t.Run(tt.description, func(t *testing.T) {
133133
db := fmt.Sprintf("%d.db", rand.Int())
134-
store, err := NewSQLLiteLoader(db, "./db_migrations")
134+
store, err := NewSQLLiteLoader(WithDBName(db))
135135
require.NoError(t, err)
136136
defer func() {
137137
if err := os.Remove(db); err != nil {

‎pkg/sqlite/migrations.go

+264
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pkg/sqlite/migrator.go

+52-7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sqlite
33
import (
44
"database/sql"
55
"fmt"
6+
"io/ioutil"
67
"os"
78
"path/filepath"
89
"sort"
@@ -14,26 +15,70 @@ import (
1415
_ "github.com/golang-migrate/migrate/v4/source/file" // indirect import required by golang-migrate package
1516
)
1617

17-
const (
18-
// Hardcoded path where the db_migrations live
19-
defaultMigrationsPath = "./pkg/sqlite/db_migrations"
20-
)
21-
2218
type SQLMigrator struct {
2319
db *sql.DB
2420
migrationsPath string
21+
generated bool
2522
}
2623

2724
// NewSQLLiteMigrator returns a SQLMigrator. The SQLMigrator takes a sql database and directory for migrations
2825
// and exposes a set of functions that allow the golang-migrate project to apply migrations to that database.
29-
func NewSQLLiteMigrator(db *sql.DB, migrationsPath string) *SQLMigrator {
26+
func NewSQLLiteMigrator(db *sql.DB, migrationsPath string) (*SQLMigrator, error) {
27+
// If no migrations folder is set, use the generated migrations
3028
if migrationsPath == "" {
31-
migrationsPath = defaultMigrationsPath
29+
// Create a temp dir for the generated migrations
30+
tempDir, err := ioutil.TempDir(".", "db_migrations_")
31+
if err != nil {
32+
return nil, err
33+
}
34+
35+
migrationsFolder := "pkg/sqlite/db_migrations"
36+
37+
dirData, err := AssetDir(migrationsFolder)
38+
if err != nil {
39+
return nil, err
40+
}
41+
42+
for _, file := range dirData {
43+
fileData, err := Asset(fmt.Sprintf("%s/%s", migrationsFolder, file))
44+
if err != nil {
45+
return nil, err
46+
}
47+
48+
f, err := os.Create(fmt.Sprintf("%s/%s", tempDir, file))
49+
if err != nil {
50+
return nil, err
51+
}
52+
defer f.Close()
53+
54+
_, err = f.Write(fileData)
55+
if err != nil {
56+
return nil, err
57+
}
58+
}
59+
60+
return &SQLMigrator{
61+
db: db,
62+
migrationsPath: tempDir,
63+
generated: true,
64+
}, nil
3265
}
66+
3367
return &SQLMigrator{
3468
db: db,
3569
migrationsPath: migrationsPath,
70+
generated: false,
71+
}, nil
72+
}
73+
74+
// CleanUpMigrator deletes any unnecessary data generated just for the scope of the migrator.
75+
// Call this function once the scope of the Migrator is no longer required
76+
func (m *SQLMigrator) CleanUpMigrator() {
77+
if m.generated {
78+
os.RemoveAll(m.migrationsPath)
3679
}
80+
81+
return
3782
}
3883

3984
// InitMigrationVersion parses the db_migrations for the latest migration version, then applies that

‎pkg/sqlite/migrator_test.go

+18-5
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import (
1111
func TestValidMigratorVersion(t *testing.T) {
1212
migrationsPath := "./db_migrations"
1313

14-
store, err := NewSQLLiteLoader("test.db", migrationsPath)
14+
store, err := NewSQLLiteLoader(WithDBName("test.db"), WithMigrationsPath(migrationsPath))
1515
require.NoError(t, err)
1616
defer os.Remove("test.db")
1717

18-
migrator := NewSQLLiteMigrator(store.db, migrationsPath)
18+
migrator, err := NewSQLLiteMigrator(store.db, migrationsPath)
19+
require.NoError(t, err, "Unable to initialize migrator")
1920

2021
version, err := migrator.CurrentVersion()
2122
require.NoError(t, err, "Could not parse latest migration version from db_migrations folder")
@@ -27,11 +28,12 @@ func TestGetMigrationVersion(t *testing.T) {
2728
expectedVersion := uint(200412250000)
2829
migrationsPath := "./testdata/test_db_migrations/valid"
2930

30-
store, err := NewSQLLiteLoader("test.db", migrationsPath)
31+
store, err := NewSQLLiteLoader(WithDBName("test.db"), WithMigrationsPath(migrationsPath))
3132
require.NoError(t, err)
3233
defer os.Remove("test.db")
3334

34-
migrator := NewSQLLiteMigrator(store.db, migrationsPath)
35+
migrator, err := NewSQLLiteMigrator(store.db, migrationsPath)
36+
require.NoError(t, err, "Unable to initialize migrator")
3537

3638
version, err := migrator.CurrentVersion()
3739
require.NoError(t, err, "Could not parse latest migration version from db_migrations folder")
@@ -43,7 +45,18 @@ func TestGetMigrationVersion(t *testing.T) {
4345
func TestGetMigrationError(t *testing.T) {
4446
migrationsPath := "./testdata/test_db_migrations/invalid"
4547

46-
_, err := NewSQLLiteLoader("test.db", migrationsPath)
48+
_, err := NewSQLLiteLoader(WithDBName("test.db"), WithMigrationsPath(migrationsPath))
4749
require.Error(t, err)
4850
defer os.Remove("test.db")
4951
}
52+
53+
func TestGeneratedMigrations(t *testing.T) {
54+
store, err := NewSQLLiteLoader(WithDBName("test.db"))
55+
require.NoError(t, err)
56+
defer os.Remove("test.db")
57+
58+
migrator, err := NewSQLLiteMigrator(store.db, "")
59+
defer migrator.CleanUpMigrator()
60+
61+
require.NoError(t, err, "Unable to initialize migrator with generated migrations")
62+
}

0 commit comments

Comments
 (0)
Please sign in to comment.