Files
threshold/internal/db/migrations/genmigrations/generate.go
T
Todd Knight f6e50006fc Generate postgres.gen.go using schema version directory structure (#808)
* Reorganize the db migration .sql scripts into individual schema version directories and generate the sql httpfs fakeFile accordingly.

* Created a /dev schema version directory to hold db schema updates that have not been finalized.

* Add an 'allow-development-migrations' flag to allow `boundary database init` to be executed even when there are schema updates in /dev.
2020-12-04 21:32:27 -07:00

150 lines
3.7 KiB
Go

package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"sort"
"strconv"
"strings"
"text/template"
)
// generate looks for migration sql in a directory for the given dialect and
// applies the templates below to the contents of the files, building up a
// migrations map for the dialect
func generate(dialect string) {
baseDir := os.Getenv("GEN_BASEPATH") + "/internal/db/migrations"
dir, err := os.Open(fmt.Sprintf("%s/%s", baseDir, dialect))
if err != nil {
fmt.Printf("error opening dir with dialect %s: %v\n", dialect, err)
os.Exit(1)
}
versions, err := dir.Readdirnames(0)
if err != nil {
fmt.Printf("error reading dir names with dialect %s: %v\n", dialect, err)
os.Exit(1)
}
outBuf := bytes.NewBuffer(nil)
valuesBuf := bytes.NewBuffer(nil)
sort.Strings(versions)
isDev := false
largestVer := 0
for _, ver := range versions {
var verVal int
switch ver {
case "dev":
verVal = largestVer + 1
default:
if verVal, err = strconv.Atoi(ver); err != nil {
fmt.Printf("error reading major schema version directory %q. Must be a number or 'dev'\n", ver)
os.Exit(1)
}
if verVal > largestVer {
largestVer = verVal
}
}
dir, err := os.Open(fmt.Sprintf("%s/%s/%s", baseDir, dialect, ver))
if err != nil {
fmt.Printf("error opening dir with dialect %s: %v\n", dialect, err)
os.Exit(1)
}
names, err := dir.Readdirnames(0)
if err != nil {
fmt.Printf("error reading dir names with dialect %s: %v\n", dialect, err)
os.Exit(1)
}
if ver == "dev" && len(names) > 0 {
isDev = true
}
sort.Strings(names)
for _, name := range names {
if !strings.HasSuffix(name, ".sql") {
continue
}
contents, err := ioutil.ReadFile(fmt.Sprintf("%s/%s/%s/%s", baseDir, dialect, ver, name))
if err != nil {
fmt.Printf("error opening file %s with dialect %s: %v", name, dialect, err)
os.Exit(1)
}
vName := name
nameParts := strings.SplitN(name, "_", 2)
if len(nameParts) != 2 {
continue
}
nameVer, err := strconv.Atoi(nameParts[0])
if err != nil {
fmt.Printf("Unable to get file version from %q\n", name)
continue
}
vName = fmt.Sprintf("%02d_%s", (verVal*1000)+nameVer, nameParts[1])
if err := migrationsValueTemplate.Execute(valuesBuf, struct {
Name string
Contents string
}{
Name: vName,
Contents: string(contents),
}); err != nil {
fmt.Printf("error executing migrations value template for file %s/%s: %s", ver, name, err)
os.Exit(1)
}
}
}
if err := migrationsTemplate.Execute(outBuf, struct {
Type string
Values string
DevMigration bool
}{
Type: dialect,
Values: valuesBuf.String(),
DevMigration: isDev,
}); err != nil {
fmt.Printf("error executing migrations value template for dialect %s: %s", dialect, err)
os.Exit(1)
}
outFile := fmt.Sprintf("%s/%s.gen.go", baseDir, dialect)
if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil {
fmt.Printf("error writing file %q: %v\n", outFile, err)
os.Exit(1)
}
}
var migrationsTemplate = template.Must(template.New("").Parse(
`// Code generated by "make migrations"; DO NOT EDIT.
package migrations
import (
"bytes"
)
// DevMigration is true if the database schema that would be applied by
// InitStore would be from files in the /dev directory which indicates it would
// not be safe to run in a non dev environment.
var DevMigration = {{ .DevMigration }}
var {{ .Type }}Migrations = map[string]*fakeFile{
"migrations": {
name: "migrations",
},
{{ .Values }}
}
`))
var migrationsValueTemplate = template.Must(template.New("").Parse(
`"migrations/{{ .Name }}": {
name: "{{ .Name }}",
bytes: []byte(` + "`\n{{ .Contents }}\n`" + `),
},
`))