aboutsummaryrefslogtreecommitdiff
path: root/cmd/goose/main.go
blob: 31a5b00503c5ba981483f3c3eaeb57f35d06530d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// This is custom goose binary

package main

import (
	"flag"
	"fmt"
	"log"
	"os"

	"github.com/pressly/goose"

	pgusers "github.com/matrix-org/dendrite/userapi/storage/postgres/deltas"
	slusers "github.com/matrix-org/dendrite/userapi/storage/sqlite3/deltas"

	_ "github.com/lib/pq"
	_ "github.com/mattn/go-sqlite3"
)

const (
	AppService       = "appservice"
	FederationSender = "federationapi"
	KeyServer        = "keyserver"
	MediaAPI         = "mediaapi"
	RoomServer       = "roomserver"
	SigningKeyServer = "signingkeyserver"
	SyncAPI          = "syncapi"
	UserAPI          = "userapi"
)

var (
	dir       = flags.String("dir", "", "directory with migration files")
	flags     = flag.NewFlagSet("goose", flag.ExitOnError)
	component = flags.String("component", "", "dendrite component name")
	knownDBs  = []string{
		AppService, FederationSender, KeyServer, MediaAPI, RoomServer, SigningKeyServer, SyncAPI, UserAPI,
	}
)

// nolint: gocyclo
func main() {
	err := flags.Parse(os.Args[1:])
	if err != nil {
		panic(err.Error())
	}
	args := flags.Args()

	if len(args) < 3 {
		fmt.Println(
			`Usage: goose [OPTIONS] DRIVER DBSTRING COMMAND

Drivers:
    postgres
    sqlite3

Examples:
    goose -component roomserver sqlite3 ./roomserver.db status
    goose -component roomserver sqlite3 ./roomserver.db up

    goose -component roomserver postgres "user=dendrite dbname=dendrite sslmode=disable" status

Options:
  -component string
    	Dendrite component name e.g roomserver, signingkeyserver, clientapi, syncapi
  -table string
    	migrations table name (default "goose_db_version")
  -h	print help
  -v	enable verbose mode
  -dir string
        directory with migration files, only relevant when creating new migrations.
  -version
    	print version

Commands:
    up                   Migrate the DB to the most recent version available
    up-by-one            Migrate the DB up by 1
    up-to VERSION        Migrate the DB to a specific VERSION
    down                 Roll back the version by 1
    down-to VERSION      Roll back to a specific VERSION
    redo                 Re-run the latest migration
    reset                Roll back all migrations
    status               Dump the migration status for the current DB
    version              Print the current version of the database
    create NAME [sql|go] Creates new migration file with the current timestamp
    fix                  Apply sequential ordering to migrations`,
		)
		return
	}

	engine := args[0]
	if engine != "sqlite3" && engine != "postgres" {
		fmt.Println("engine must be one of 'sqlite3' or 'postgres'")
		return
	}

	knownComponent := false
	for _, c := range knownDBs {
		if c == *component {
			knownComponent = true
			break
		}
	}
	if !knownComponent {
		fmt.Printf("component must be one of %v\n", knownDBs)
		return
	}

	if engine == "sqlite3" {
		loadSQLiteDeltas(*component)
	} else {
		loadPostgresDeltas(*component)
	}

	dbstring, command := args[1], args[2]

	db, err := goose.OpenDBWithDriver(engine, dbstring)
	if err != nil {
		log.Fatalf("goose: failed to open DB: %v\n", err)
	}

	defer func() {
		if err := db.Close(); err != nil {
			log.Fatalf("goose: failed to close DB: %v\n", err)
		}
	}()

	arguments := []string{}
	if len(args) > 3 {
		arguments = append(arguments, args[3:]...)
	}

	// goose demands a directory even though we don't use it for upgrades
	d := *dir
	if d == "" {
		d = os.TempDir()
	}
	if err := goose.Run(command, db, d, arguments...); err != nil {
		log.Fatalf("goose %v: %v", command, err)
	}
}

func loadSQLiteDeltas(component string) {
	switch component {
	case UserAPI:
		slusers.LoadFromGoose()
	}
}

func loadPostgresDeltas(component string) {
	switch component {
	case UserAPI:
		pgusers.LoadFromGoose()
	}
}