zitadel/internal/migration/migration.go
Livio Amstutz 56b916a2b0
feat: projections auto create their tables (#3324)
* begin init checks for projections

* first projection checks

* debug notification providers with query fixes

* more projections and first index

* more projections

* more projections

* finish projections

* fix tests (remove db name)

* create tables in setup

* fix logging / error handling

* add tenant to views

* rename tenant to instance_id

* add instance_id to all projections

* add instance_id to all queries

* correct instance_id on projections

* add instance_id to failed_events

* use separate context for instance

* implement features projection

* implement features projection

* remove unique constraint from setup when migration failed

* add error to failed setup event

* add instance_id to primary keys

* fix IAM projection

* remove old migrations folder

* fix keysFromYAML test
2022-03-23 09:02:39 +01:00

85 lines
2.0 KiB
Go

package migration
import (
"context"
"encoding/json"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
)
const (
startedType = eventstore.EventType("system.migration.started")
doneType = eventstore.EventType("system.migration.done")
failedType = eventstore.EventType("system.migration.failed")
aggregateType = eventstore.AggregateType("system")
aggregateID = "SYSTEM"
)
type Migration interface {
String() string
Execute(context.Context) error
}
func Migrate(ctx context.Context, es *eventstore.Eventstore, migration Migration) (err error) {
if should, err := shouldExec(ctx, es, migration); !should || err != nil {
return err
}
if _, err = es.Push(ctx, setupStartedCmd(migration)); err != nil {
return err
}
err = migration.Execute(ctx)
logging.OnError(err).Error("migration failed")
_, err = es.Push(ctx, setupDoneCmd(migration, err))
return err
}
func shouldExec(ctx context.Context, es *eventstore.Eventstore, migration Migration) (should bool, err error) {
events, err := es.Filter(ctx, eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
OrderDesc().
AddQuery().
AggregateTypes(aggregateType).
AggregateIDs(aggregateID).
EventTypes(startedType, doneType, failedType).
Builder())
if err != nil {
return false, err
}
if len(events) == 0 {
return true, nil
}
if events[len(events)-1].Type() == startedType {
return false, nil
}
for _, e := range events {
step := new(SetupStep)
err = json.Unmarshal(e.DataAsBytes(), step)
if err != nil {
return false, err
}
if step.Name != migration.String() {
continue
}
switch e.Type() {
case startedType, doneType:
//TODO: if started should we wait until done/failed?
return false, nil
case failedType:
//TODO: how to allow retries?
logging.WithFields("migration", migration.String()).Error("failed before")
return false, errors.ThrowInternal(nil, "MIGRA-mjI2E", "migration failed before")
}
}
return true, nil
}