Passed
Pull Request — master (#1258)
by Tolga
02:36
created

internal/storage/migration.go   C

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
cc 54
eloc 166
dl 0
loc 278
rs 6.4799
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
B storage.MigrateStatus 0 28 7
B storage.MigrateReset 0 28 7
B storage.MigrateUpTo 0 28 7
A storage.closeDB 0 3 2
B storage.MigrateUp 0 28 7
C storage.Migrate 0 52 10
B storage.MigrateDown 0 28 7
B storage.MigrateDownTo 0 28 7
1
package storage
2
3
import (
4
	"embed"
5
	"fmt"
6
	"log"
7
8
	"github.com/jackc/pgx/v5/stdlib"
9
10
	"github.com/pressly/goose/v3"
11
12
	"github.com/Permify/permify/internal/config"
13
	"github.com/Permify/permify/internal/storage/postgres/utils"
14
	"github.com/Permify/permify/pkg/database"
15
	PQDatabase "github.com/Permify/permify/pkg/database/postgres"
16
)
17
18
const (
19
	postgresMigrationDir = "postgres/migrations"
20
	postgresDialect      = "postgres"
21
	migrationsTable      = "migrations"
22
)
23
24
//go:embed postgres/migrations/*.sql
25
var postgresMigrations embed.FS
26
27
// Migrate performs database migrations depending on the given configuration.
28
func Migrate(conf config.Database) (err error) {
29
	switch conf.Engine {
30
	case database.POSTGRES.String():
31
		// Create a new Postgres database connection
32
		var db *PQDatabase.Postgres
33
34
		if conf.URI == "" {
35
			db, err = PQDatabase.NewWithSeparateURIs(conf.Writer.URI, conf.Reader.URI)
36
			if err != nil {
37
				return err
38
			}
39
		} else {
40
			db, err = PQDatabase.New(conf.URI)
41
			if err != nil {
42
				return err
43
			}
44
		}
45
46
		// Ensure database connection is closed when function returns
47
		defer closeDB(db)
48
49
		// check postgres version
50
		_, err = utils.EnsureDBVersion(db.ReadPool)
51
		if err != nil {
52
			return err
53
		}
54
55
		// Set table name for migrations
56
		goose.SetTableName(migrationsTable)
57
58
		// Set dialect to be used for migration
59
		if err = goose.SetDialect(postgresDialect); err != nil {
60
			return err
61
		}
62
63
		// Set file system for migration scripts
64
		goose.SetBaseFS(postgresMigrations)
65
66
		pool := stdlib.OpenDBFromPool(db.WritePool)
67
68
		// Perform migration
69
		if err = goose.Up(pool, postgresMigrationDir); err != nil {
70
			return err
71
		}
72
73
		return nil
74
	case database.MEMORY.String():
75
		// No migrations needed for in-memory database
76
		return nil
77
	default:
78
		// Unsupported database engine
79
		return fmt.Errorf("%s connection is unsupported", conf.Engine)
80
	}
81
}
82
83
// MigrateUp performs all available database migrations to update the schema to the latest version.
84
func MigrateUp(engine, uri string) (err error) {
85
	switch engine {
86
	case database.POSTGRES.String():
87
		var db *PQDatabase.Postgres
88
		db, err = PQDatabase.New(uri)
89
		if err != nil {
90
			return err
91
		}
92
		defer closeDB(db)
93
94
		goose.SetTableName(migrationsTable)
95
96
		if err = goose.SetDialect(postgresDialect); err != nil {
97
			return err
98
		}
99
100
		goose.SetBaseFS(postgresMigrations)
101
		pool := stdlib.OpenDBFromPool(db.WritePool)
102
103
		if err = goose.Up(pool, postgresMigrationDir); err != nil {
104
			return err
105
		}
106
107
		return nil
108
	case database.MEMORY.String():
109
		return nil
110
	default:
111
		return fmt.Errorf("%s connection is unsupported", engine)
112
	}
113
}
114
115
// MigrateUpTo performs database migrations up to a specific version.
116
func MigrateUpTo(engine, uri string, p int64) (err error) {
117
	switch engine {
118
	case database.POSTGRES.String():
119
		var db *PQDatabase.Postgres
120
		db, err = PQDatabase.New(uri)
121
		if err != nil {
122
			return err
123
		}
124
		defer closeDB(db)
125
126
		goose.SetTableName(migrationsTable)
127
128
		if err = goose.SetDialect(postgresDialect); err != nil {
129
			return err
130
		}
131
132
		goose.SetBaseFS(postgresMigrations)
133
		pool := stdlib.OpenDBFromPool(db.WritePool)
134
135
		if err = goose.UpTo(pool, postgresMigrationDir, p); err != nil {
136
			return err
137
		}
138
139
		return nil
140
	case database.MEMORY.String():
141
		return nil
142
	default:
143
		return fmt.Errorf("%s connection is unsupported", engine)
144
	}
145
}
146
147
// MigrateDown undoes all database migrations, reverting the schema to the initial state.
148
func MigrateDown(engine, uri string) (err error) {
149
	switch engine {
150
	case database.POSTGRES.String():
151
		var db *PQDatabase.Postgres
152
		db, err = PQDatabase.New(uri)
153
		if err != nil {
154
			return err
155
		}
156
		defer closeDB(db)
157
158
		goose.SetTableName(migrationsTable)
159
160
		if err = goose.SetDialect(postgresDialect); err != nil {
161
			return err
162
		}
163
164
		goose.SetBaseFS(postgresMigrations)
165
		pool := stdlib.OpenDBFromPool(db.WritePool)
166
167
		if err = goose.Down(pool, postgresMigrationDir); err != nil {
168
			return err
169
		}
170
171
		return nil
172
	case database.MEMORY.String():
173
		return nil
174
	default:
175
		return fmt.Errorf("%s connection is unsupported", engine)
176
	}
177
}
178
179
// MigrateDownTo undoes database migrations down to a specific version.
180
func MigrateDownTo(engine, uri string, p int64) (err error) {
181
	switch engine {
182
	case database.POSTGRES.String():
183
		var db *PQDatabase.Postgres
184
		db, err = PQDatabase.New(uri)
185
		if err != nil {
186
			return err
187
		}
188
		defer closeDB(db)
189
190
		goose.SetTableName(migrationsTable)
191
192
		if err = goose.SetDialect(postgresDialect); err != nil {
193
			return err
194
		}
195
196
		goose.SetBaseFS(postgresMigrations)
197
		pool := stdlib.OpenDBFromPool(db.WritePool)
198
199
		if err = goose.DownTo(pool, postgresMigrationDir, p); err != nil {
200
			return err
201
		}
202
203
		return nil
204
	case database.MEMORY.String():
205
		return nil
206
	default:
207
		return fmt.Errorf("%s connection is unsupported", engine)
208
	}
209
}
210
211
// MigrateReset roll back all migrations.
212
func MigrateReset(engine, uri string) (err error) {
213
	switch engine {
214
	case database.POSTGRES.String():
215
		var db *PQDatabase.Postgres
216
		db, err = PQDatabase.New(uri)
217
		if err != nil {
218
			return err
219
		}
220
		defer closeDB(db)
221
222
		goose.SetTableName(migrationsTable)
223
224
		if err = goose.SetDialect(postgresDialect); err != nil {
225
			return err
226
		}
227
228
		goose.SetBaseFS(postgresMigrations)
229
		pool := stdlib.OpenDBFromPool(db.WritePool)
230
231
		if err = goose.Reset(pool, postgresMigrationDir); err != nil {
232
			return err
233
		}
234
235
		return nil
236
	case database.MEMORY.String():
237
		return nil
238
	default:
239
		return fmt.Errorf("%s connection is unsupported", engine)
240
	}
241
}
242
243
// MigrateStatus displays the status of all migrations.
244
func MigrateStatus(engine, uri string) (err error) {
245
	switch engine {
246
	case database.POSTGRES.String():
247
		var db *PQDatabase.Postgres
248
		db, err = PQDatabase.New(uri)
249
		if err != nil {
250
			return err
251
		}
252
		defer closeDB(db)
253
254
		goose.SetTableName(migrationsTable)
255
256
		if err = goose.SetDialect(postgresDialect); err != nil {
257
			return err
258
		}
259
260
		goose.SetBaseFS(postgresMigrations)
261
		pool := stdlib.OpenDBFromPool(db.WritePool)
262
263
		if err = goose.Status(pool, postgresMigrationDir); err != nil {
264
			return err
265
		}
266
267
		return nil
268
	case database.MEMORY.String():
269
		return nil
270
	default:
271
		return fmt.Errorf("%s connection is unsupported", engine)
272
	}
273
}
274
275
// closeDB cleanly closes the database connection and logs if an error occurs.
276
func closeDB(db *PQDatabase.Postgres) {
277
	if err := db.Close(); err != nil {
278
		log.Printf("failed to close the database: %v", err)
279
	}
280
}
281