Skip to content

Commit

Permalink
feat: fixup migration visiblity for ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
zinic committed Feb 9, 2024
1 parent c4cbd24 commit 4b2fe85
Show file tree
Hide file tree
Showing 10 changed files with 96 additions and 12 deletions.
5 changes: 5 additions & 0 deletions cmd/api/src/database/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ type Database interface {
RawFirst(value any) error
Wipe() error
Migrate() error
RequiresMigration() (bool, error)
CreateAuditLog(auditLog model.AuditLog) error
AppendAuditLog(ctx context.Context, entry model.AuditEntry) error
ListAuditLogs(before, after time.Time, offset, limit int, order string, filter model.SQLFilter) (model.AuditLogs, int, error)
Expand Down Expand Up @@ -226,6 +227,10 @@ func (s *BloodhoundDB) Wipe() error {
})
}

func (s *BloodhoundDB) RequiresMigration() (bool, error) {
return migration.NewMigrator(s.db).RequiresMigration()
}

func (s *BloodhoundDB) Migrate() error {
// Run the migrator
if err := migration.NewMigrator(s.db).Migrate(); err != nil {
Expand Down
18 changes: 18 additions & 0 deletions cmd/api/src/database/migration/stepwise.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,24 @@ ALTER TABLE ONLY migrations ADD CONSTRAINT migrations_pkey PRIMARY KEY (id);`
return nil
}

func (s *Migrator) RequiresMigration() (bool, error) {
// check if migration table exists to determine type of manifest to generate
if hasTable, err := s.HasMigrationTable(); err != nil {
return false, fmt.Errorf("failed to check if migration table exists: %w", err)
} else if !hasTable {
// no migration table, assume this is new installation and requires migration
return true, nil
}

if lastMigration, err := s.LatestMigration(); err != nil {
return false, fmt.Errorf("could not get latest migration: %w", err)
} else if manifest, err := s.GenerateManifestAfterVersion(lastMigration.Version()); err != nil {
return false, fmt.Errorf("failed to generate migration manifest from previous version: %w", err)
} else {
return len(manifest.VersionTable) > 0, nil
}
}

// executeStepwiseMigrations will run all necessary migrations for a deployment.
// It begins by checking if migration schema exists. If it does not, we assume the
// deployment is a new installation, otherwise we assume it may have migration updates.
Expand Down
15 changes: 15 additions & 0 deletions cmd/api/src/database/mocks/db.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cmd/api/src/services/entrypoint.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ func Entrypoint(ctx context.Context, cfg config.Configuration, connections boots
} else if err := bootstrap.MigrateGraph(ctx, connections.Graph, schema.DefaultGraphSchema()); err != nil {
return nil, fmt.Errorf("graph migration error: %w", err)
}
} else if err := connections.Graph.SetDefaultGraph(ctx, schema.DefaultGraph()); err != nil {
return nil, fmt.Errorf("no default graph found but migrations are disabled per configuration: %w", err)
} else {
log.Infof("Database migrations are disabled per configuration")
}
Expand Down
4 changes: 4 additions & 0 deletions packages/go/dawgs/drivers/neo4j/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ func (s *driver) AssertSchema(ctx context.Context, schema graph.Schema) error {
return assertSchema(ctx, s, schema)
}

func (s *driver) SetDefaultGraph(ctx context.Context, schema graph.Graph) error {
return nil
}

func (s *driver) Run(ctx context.Context, query string, parameters map[string]any) error {
return s.WriteTransaction(ctx, func(tx graph.Transaction) error {
result := tx.Raw(query, parameters)
Expand Down
26 changes: 14 additions & 12 deletions packages/go/dawgs/drivers/pg/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,16 +58,16 @@ type Driver struct {
batchWriteSize int
}

func (s *Driver) KindMapper() KindMapper {
return s.schemaManager
}

func (s *Driver) SetDefaultGraph(ctx context.Context, graphSchema graph.Graph) error {
return s.WriteTransaction(ctx, func(tx graph.Transaction) error {
return s.schemaManager.AssertDefaultGraph(tx, graphSchema)
return s.ReadTransaction(ctx, func(tx graph.Transaction) error {
return s.schemaManager.SetDefaultGraph(tx, graphSchema)
})
}

func (s *Driver) KindMapper() KindMapper {
return s.schemaManager
}

func (s *Driver) SetBatchWriteSize(size int) {
s.batchWriteSize = size
}
Expand Down Expand Up @@ -177,19 +177,21 @@ func (s *Driver) FetchSchema(ctx context.Context) (graph.Schema, error) {

func (s *Driver) AssertSchema(ctx context.Context, schema graph.Schema) error {
if err := s.WriteTransaction(ctx, func(tx graph.Transaction) error {
return s.schemaManager.AssertSchema(tx, schema)
if err := s.schemaManager.AssertSchema(tx, schema); err != nil {
return err
} else if schema.DefaultGraph.Name != "" {
return s.schemaManager.AssertDefaultGraph(tx, schema.DefaultGraph)
}

return nil
}, OptionSetQueryExecMode(pgx.QueryExecModeSimpleProtocol)); err != nil {
return err
} else {
// Resetting the pool must be done on every schema assertion as composite types may have changed OIDs
s.pool.Reset()
}

if schema.DefaultGraph.Name == "" {
return nil
}

return s.SetDefaultGraph(ctx, schema.DefaultGraph)
return nil
}

func (s *Driver) Run(ctx context.Context, query string, parameters map[string]any) error {
Expand Down
14 changes: 14 additions & 0 deletions packages/go/dawgs/drivers/pg/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,20 @@ func (s *SchemaManager) AssertKinds(tx graph.Transaction, kinds graph.Kinds) ([]
return kindIDs, nil
}

func (s *SchemaManager) SetDefaultGraph(tx graph.Transaction, schema graph.Graph) error {
// Validate the schema if the graph already exists in the database
if definition, err := query.On(tx).SelectGraphByName(schema.Name); err != nil {
return err
} else {
s.graphs[schema.Name] = definition

s.defaultGraph = definition
s.hasDefaultGraph = true
}

return nil
}

func (s *SchemaManager) AssertDefaultGraph(tx graph.Transaction, schema graph.Graph) error {
if graphInstance, err := s.AssertGraph(tx, schema); err != nil {
return err
Expand Down
3 changes: 3 additions & 0 deletions packages/go/dawgs/graph/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,9 @@ type Database interface {
// AssertSchema will apply the given schema to the underlying database.
AssertSchema(ctx context.Context, dbSchema Schema) error

// SetDefaultGraph sets the default graph namespace for the connection.
SetDefaultGraph(ctx context.Context, graphSchema Graph) error

// Run allows a user to pass statements directly to the database. Since results may rely on a transactional context
// only an error is returned from this function
Run(ctx context.Context, query string, parameters map[string]any) error
Expand Down
14 changes: 14 additions & 0 deletions packages/go/dawgs/graph/mocks/graph.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions packages/go/dawgs/graph/switch.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ func NewDatabaseSwitch(ctx context.Context, initialDB Database) *DatabaseSwitch
}
}

func (s *DatabaseSwitch) SetDefaultGraph(ctx context.Context, graphSchema Graph) error {
s.currentDBLock.RLock()
defer s.currentDBLock.RUnlock()

return s.currentDB.SetDefaultGraph(ctx, graphSchema)
}

func (s *DatabaseSwitch) Switch(db Database) {
s.inSwitch = true

Expand Down

0 comments on commit 4b2fe85

Please sign in to comment.