mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-01 00:37:24 +00:00
fix: v2 setup sequence (#3437)
* add/register human command done * validations * crypto * move clientid * keys * fix: clientID * remove v2 package * tests * tests running * fix: add init instance to eventstore * fix: mig * test(eventstore): create instance * revert old code * instance domain from ctx * chore: rename zitadel app ids * comments * fix: test * fix: mock * fix: test
This commit is contained in:
parent
375a57377d
commit
db554536a1
@ -28,7 +28,7 @@ Prereqesits:
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
config := MustNewConfig(viper.New())
|
config := MustNewConfig(viper.New())
|
||||||
|
|
||||||
err := initialise(config, VerifyGrant(config.Database.Database, config.Database.User.Username))
|
err := initialise(config, VerifyGrant(config.Database.Database, config.Database.Username))
|
||||||
logging.OnError(err).Fatal("unable to set grant")
|
logging.OnError(err).Fatal("unable to set grant")
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ Prereqesits:
|
|||||||
|
|
||||||
func VerifyGrant(database, username string) func(*sql.DB) error {
|
func VerifyGrant(database, username string) func(*sql.DB) error {
|
||||||
return func(db *sql.DB) error {
|
return func(db *sql.DB) error {
|
||||||
logging.WithFields("user", username).Info("verify grant")
|
logging.WithFields("user", username, "database", database).Info("verify grant")
|
||||||
return verify(db,
|
return verify(db,
|
||||||
exists(fmt.Sprintf(searchGrant, database), username),
|
exists(fmt.Sprintf(searchGrant, database), username),
|
||||||
exec(fmt.Sprintf(grantStmt, database, username)),
|
exec(fmt.Sprintf(grantStmt, database, username)),
|
||||||
|
@ -3,6 +3,7 @@ package setup
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
@ -49,6 +50,12 @@ func Setup(config *Config, steps *Steps, masterKey string) {
|
|||||||
|
|
||||||
steps.s1ProjectionTable = &ProjectionTable{dbClient: dbClient}
|
steps.s1ProjectionTable = &ProjectionTable{dbClient: dbClient}
|
||||||
steps.s2AssetsTable = &AssetTable{dbClient: dbClient}
|
steps.s2AssetsTable = &AssetTable{dbClient: dbClient}
|
||||||
|
|
||||||
|
steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = strings.TrimSpace(steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address)
|
||||||
|
if steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address == "" {
|
||||||
|
steps.S3DefaultInstance.InstanceSetup.Org.Human.Email.Address = "admin@" + config.ExternalDomain
|
||||||
|
}
|
||||||
|
|
||||||
steps.S3DefaultInstance.es = eventstoreClient
|
steps.S3DefaultInstance.es = eventstoreClient
|
||||||
steps.S3DefaultInstance.db = dbClient
|
steps.S3DefaultInstance.db = dbClient
|
||||||
steps.S3DefaultInstance.defaults = config.SystemDefaults
|
steps.S3DefaultInstance.defaults = config.SystemDefaults
|
||||||
|
@ -9,7 +9,7 @@ S3DefaultInstance:
|
|||||||
NickName:
|
NickName:
|
||||||
DisplayName:
|
DisplayName:
|
||||||
Email:
|
Email:
|
||||||
Address: admin@zitadel.ch
|
Address: #autogenerated if empty. uses domain from config and prefixes admin@. for example: admin@domain.tdl
|
||||||
Verified: true
|
Verified: true
|
||||||
PreferredLanguage:
|
PreferredLanguage:
|
||||||
Gender:
|
Gender:
|
||||||
|
@ -162,6 +162,12 @@ func (c *commandNew) SetUpInstance(ctx context.Context, setup *InstanceSetup) (*
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = c.es.NewInstance(ctx, instanceID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx = authz.SetCtxData(authz.WithInstanceID(ctx, instanceID), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID})
|
||||||
requestedDomain := authz.GetInstance(ctx).RequestedDomain()
|
requestedDomain := authz.GetInstance(ctx).RequestedDomain()
|
||||||
ctx = authz.SetCtxData(authz.WithRequestedDomain(authz.WithInstanceID(ctx, instanceID), requestedDomain), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID})
|
ctx = authz.SetCtxData(authz.WithRequestedDomain(authz.WithInstanceID(ctx, instanceID), requestedDomain), authz.CtxData{OrgID: instanceID, ResourceOwner: instanceID})
|
||||||
|
|
||||||
|
@ -59,6 +59,10 @@ func (es *Eventstore) Push(ctx context.Context, cmds ...Command) ([]Event, error
|
|||||||
return eventReaders, nil
|
return eventReaders, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (es *Eventstore) NewInstance(ctx context.Context, instanceID string) error {
|
||||||
|
return es.repo.CreateInstance(ctx, instanceID)
|
||||||
|
}
|
||||||
|
|
||||||
func commandsToRepository(instanceID string, cmds []Command) (events []*repository.Event, constraints []*repository.UniqueConstraint, err error) {
|
func commandsToRepository(instanceID string, cmds []Command) (events []*repository.Event, constraints []*repository.UniqueConstraint, err error) {
|
||||||
events = make([]*repository.Event, len(cmds))
|
events = make([]*repository.Event, len(cmds))
|
||||||
for i, cmd := range cmds {
|
for i, cmd := range cmds {
|
||||||
@ -245,7 +249,3 @@ func uniqueConstraintActionToRepository(action UniqueConstraintAction) repositor
|
|||||||
return repository.UniqueConstraintAdd
|
return repository.UniqueConstraintAdd
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *Eventstore) Step20(ctx context.Context, latestSequence uint64) error {
|
|
||||||
return es.repo.Step20(ctx, latestSequence)
|
|
||||||
}
|
|
||||||
|
@ -698,6 +698,10 @@ func (repo *testRepo) Health(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *testRepo) CreateInstance(ctx context.Context, instance string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (repo *testRepo) Step20(context.Context, uint64) error { return nil }
|
func (repo *testRepo) Step20(context.Context, uint64) error { return nil }
|
||||||
|
|
||||||
func (repo *testRepo) Push(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error {
|
func (repo *testRepo) Push(ctx context.Context, events []*repository.Event, uniqueConstraints ...*repository.UniqueConstraint) error {
|
||||||
|
@ -35,6 +35,20 @@ func (m *MockRepository) EXPECT() *MockRepositoryMockRecorder {
|
|||||||
return m.recorder
|
return m.recorder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateInstance mocks base method.
|
||||||
|
func (m *MockRepository) CreateInstance(arg0 context.Context, arg1 string) error {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "CreateInstance", arg0, arg1)
|
||||||
|
ret0, _ := ret[0].(error)
|
||||||
|
return ret0
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateInstance indicates an expected call of CreateInstance.
|
||||||
|
func (mr *MockRepositoryMockRecorder) CreateInstance(arg0, arg1 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateInstance", reflect.TypeOf((*MockRepository)(nil).CreateInstance), arg0, arg1)
|
||||||
|
}
|
||||||
|
|
||||||
// Filter mocks base method.
|
// Filter mocks base method.
|
||||||
func (m *MockRepository) Filter(arg0 context.Context, arg1 *repository.SearchQuery) ([]*repository.Event, error) {
|
func (m *MockRepository) Filter(arg0 context.Context, arg1 *repository.SearchQuery) ([]*repository.Event, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
@ -97,17 +111,3 @@ func (mr *MockRepositoryMockRecorder) Push(arg0, arg1 interface{}, arg2 ...inter
|
|||||||
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
varargs := append([]interface{}{arg0, arg1}, arg2...)
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Push", reflect.TypeOf((*MockRepository)(nil).Push), varargs...)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Push", reflect.TypeOf((*MockRepository)(nil).Push), varargs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step20 mocks base method.
|
|
||||||
func (m *MockRepository) Step20(arg0 context.Context, arg1 uint64) error {
|
|
||||||
m.ctrl.T.Helper()
|
|
||||||
ret := m.ctrl.Call(m, "Step20", arg0, arg1)
|
|
||||||
ret0, _ := ret[0].(error)
|
|
||||||
return ret0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step20 indicates an expected call of Step20.
|
|
||||||
func (mr *MockRepositoryMockRecorder) Step20(arg0, arg1 interface{}) *gomock.Call {
|
|
||||||
mr.mock.ctrl.T.Helper()
|
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Step20", reflect.TypeOf((*MockRepository)(nil).Step20), arg0, arg1)
|
|
||||||
}
|
|
||||||
|
@ -16,6 +16,6 @@ type Repository interface {
|
|||||||
Filter(ctx context.Context, searchQuery *SearchQuery) (events []*Event, err error)
|
Filter(ctx context.Context, searchQuery *SearchQuery) (events []*Event, err error)
|
||||||
//LatestSequence returns the latests sequence found by the the search query
|
//LatestSequence returns the latests sequence found by the the search query
|
||||||
LatestSequence(ctx context.Context, queryFactory *SearchQuery) (uint64, error)
|
LatestSequence(ctx context.Context, queryFactory *SearchQuery) (uint64, error)
|
||||||
|
//CreateInstance creates a new sequence for the given instance
|
||||||
Step20(ctx context.Context, latestSequence uint64) error
|
CreateInstance(ctx context.Context, instanceID string) error
|
||||||
}
|
}
|
||||||
|
@ -153,6 +153,25 @@ func (db *CRDB) Push(ctx context.Context, events []*repository.Event, uniqueCons
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var instanceRegexp = regexp.MustCompile(`eventstore\.i_[0-9a-zA-Z]{1,}_seq`)
|
||||||
|
|
||||||
|
func (db *CRDB) CreateInstance(ctx context.Context, instanceID string) error {
|
||||||
|
row := db.client.QueryRowContext(ctx, "SELECT CONCAT('eventstore.i_', $1, '_seq')", instanceID)
|
||||||
|
if row.Err() != nil {
|
||||||
|
return caos_errs.ThrowInvalidArgument(row.Err(), "SQL-7gtFA", "Errors.InvalidArgument")
|
||||||
|
}
|
||||||
|
var sequenceName string
|
||||||
|
if err := row.Scan(&sequenceName); err != nil || !instanceRegexp.MatchString(sequenceName) {
|
||||||
|
return caos_errs.ThrowInvalidArgument(err, "SQL-7gtFA", "Errors.InvalidArgument")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := db.client.ExecContext(ctx, "CREATE SEQUENCE "+sequenceName); err != nil {
|
||||||
|
return caos_errs.ThrowInternal(err, "SQL-7gtFA", "Errors.Internal")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// handleUniqueConstraints adds or removes unique constraints
|
// handleUniqueConstraints adds or removes unique constraints
|
||||||
func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueConstraints ...*repository.UniqueConstraint) (err error) {
|
func (db *CRDB) handleUniqueConstraints(ctx context.Context, tx *sql.Tx, uniqueConstraints ...*repository.UniqueConstraint) (err error) {
|
||||||
if len(uniqueConstraints) == 0 || (len(uniqueConstraints) == 1 && uniqueConstraints[0] == nil) {
|
if len(uniqueConstraints) == 0 || (len(uniqueConstraints) == 1 && uniqueConstraints[0] == nil) {
|
||||||
|
@ -561,6 +561,84 @@ func TestCRDB_Push_MultipleAggregate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCRDB_CreateInstance(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
instanceID string
|
||||||
|
}
|
||||||
|
type res struct {
|
||||||
|
wantErr bool
|
||||||
|
exists bool
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
res res
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no number",
|
||||||
|
args: args{
|
||||||
|
instanceID: "asdf;use defaultdb;DROP DATABASE zitadel;--",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
wantErr: true,
|
||||||
|
exists: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no instance id",
|
||||||
|
args: args{
|
||||||
|
instanceID: "",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
wantErr: true,
|
||||||
|
exists: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "correct number",
|
||||||
|
args: args{
|
||||||
|
instanceID: "1235",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
wantErr: false,
|
||||||
|
exists: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "correct text",
|
||||||
|
args: args{
|
||||||
|
instanceID: "system",
|
||||||
|
},
|
||||||
|
res: res{
|
||||||
|
wantErr: false,
|
||||||
|
exists: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
db := &CRDB{
|
||||||
|
client: testCRDBClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := db.CreateInstance(context.Background(), tt.args.instanceID); (err != nil) != tt.res.wantErr {
|
||||||
|
t.Errorf("CRDB.CreateInstance() error = %v, wantErr %v", err, tt.res.wantErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
sequenceRow := testCRDBClient.QueryRow("SELECT EXISTS(SELECT 1 FROM [SHOW SEQUENCES FROM eventstore] WHERE sequence_name like $1)", "i_"+tt.args.instanceID+"%")
|
||||||
|
var exists bool
|
||||||
|
err := sequenceRow.Scan(&exists)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("unable to query inserted rows: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if exists != tt.res.exists {
|
||||||
|
t.Errorf("expected exists %v got %v", tt.res.exists, exists)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCRDB_Push_Parallel(t *testing.T) {
|
func TestCRDB_Push_Parallel(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
events [][]*repository.Event
|
events [][]*repository.Event
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
package sql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
|
|
||||||
"github.com/caos/logging"
|
|
||||||
repo "github.com/caos/zitadel/internal/eventstore/repository"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (db *CRDB) Step20(ctx context.Context, latestSequence uint64) error {
|
|
||||||
currentSequence := uint64(0)
|
|
||||||
limit := uint64(500)
|
|
||||||
previousSequences := make(map[repo.AggregateType]Sequence)
|
|
||||||
for currentSequence < latestSequence {
|
|
||||||
events, err := db.Filter(ctx, &repo.SearchQuery{
|
|
||||||
Columns: repo.ColumnsEvent,
|
|
||||||
Limit: limit,
|
|
||||||
Filters: [][]*repo.Filter{
|
|
||||||
{
|
|
||||||
&repo.Filter{
|
|
||||||
Field: repo.FieldSequence,
|
|
||||||
Operation: repo.OperationGreater,
|
|
||||||
Value: currentSequence,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tx, err := db.client.Begin()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, event := range events {
|
|
||||||
if _, err := tx.Exec("SAVEPOINT event_update"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
seq := Sequence(previousSequences[event.AggregateType])
|
|
||||||
if _, err = tx.Exec("UPDATE eventstore.events SET previous_aggregate_type_sequence = $1 WHERE event_sequence = $2", &seq, event.Sequence); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = tx.Exec("RELEASE SAVEPOINT event_update"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
previousSequences[event.AggregateType] = Sequence(event.Sequence)
|
|
||||||
currentSequence = event.Sequence
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = tx.Commit(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logging.WithFields("currentSeq", currentSequence, "events", len(events)).Info("events updated")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user