import (
"context"
+ "fmt"
"io/fs"
"os"
"path/filepath"
// there are no new migrations, that would add ~2s to startup
// time / downtime during service restart.
- todo := map[string]bool{}
-
- // list versions in db/migrate/{version}_{name}.rb
- fs.WalkDir(os.DirFS(appdir), "db/migrate", func(path string, d fs.DirEntry, err error) error {
- fnm := d.Name()
- if !strings.HasSuffix(fnm, ".rb") {
- return nil
- }
- for i, c := range fnm {
- if i > 0 && c == '_' {
- todo[fnm[:i]] = true
- break
- }
- if c < '0' || c > '9' {
- // non-numeric character before the
- // first '_' means this is not a
- // migration
- break
- }
- }
- return nil
- })
+ todo, err := migrationList(appdir)
+ if err != nil {
+ return err
+ }
// read schema_migrations table (list of migrations already
// applied) and remove those entries from todo
defer dblock.RailsMigrations.Unlock()
return super.RunProgram(ctx, appdir, runOptions{env: railsEnv}, "bundle", "exec", "rake", "db:migrate")
}
+
+func migrationList(dir string) (map[string]bool, error) {
+ todo := map[string]bool{}
+
+ // list versions in db/migrate/{version}_{name}.rb
+ err := fs.WalkDir(os.DirFS(dir), "db/migrate", func(path string, d fs.DirEntry, err error) error {
+ if d.IsDir() {
+ return nil
+ }
+ fnm := d.Name()
+ if !strings.HasSuffix(fnm, ".rb") {
+ return fmt.Errorf("unexpected file in db/migrate dir: %s", fnm)
+ }
+ for i, c := range fnm {
+ if i > 0 && c == '_' {
+ todo[fnm[:i]] = true
+ break
+ }
+ if c < '0' || c > '9' {
+ // non-numeric character before the
+ // first '_' means this is not a
+ // migration
+ return fmt.Errorf("unexpected file in db/migrate dir: %s", fnm)
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ return nil, err
+ }
+ return todo, nil
+}
--- /dev/null
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+package boot
+
+import (
+ "git.arvados.org/arvados.git/lib/config"
+ "git.arvados.org/arvados.git/sdk/go/arvadostest"
+ "git.arvados.org/arvados.git/sdk/go/ctxlog"
+ "gopkg.in/check.v1"
+)
+
+type railsDBSuite struct{}
+
+var _ = check.Suite(&railsDBSuite{})
+
+// Check services/api/db/migrate/*.rb match schema_migrations
+func (s *railsDBSuite) TestMigrationList(c *check.C) {
+ todo, err := migrationList("../../services/api")
+ c.Check(err, check.IsNil)
+ c.Check(todo["20220804133317"], check.Equals, true)
+
+ cfg, err := config.NewLoader(nil, ctxlog.TestLogger(c)).Load()
+ c.Assert(err, check.IsNil)
+ cluster, err := cfg.GetCluster("")
+ c.Assert(err, check.IsNil)
+ db := arvadostest.DB(c, cluster)
+ rows, err := db.Query(`SELECT version FROM schema_migrations`)
+ for rows.Next() {
+ var v string
+ err = rows.Scan(&v)
+ c.Assert(err, check.IsNil)
+ if !todo[v] {
+ c.Errorf("version is in schema_migrations but not services/api/db/migrate/: %q", v)
+ }
+ delete(todo, v)
+ }
+ err = rows.Close()
+ c.Assert(err, check.IsNil)
+
+ // In the test suite, the database should be fully migrated.
+ // So, if there's anything left in todo here, there is
+ // something wrong with our "db/migrate/*.rb ==
+ // schema_migrations" reasoning.
+ c.Check(todo, check.HasLen, 0)
+}