mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 20:57:24 +00:00
fix: init sub commands (#3218)
* fix(init): add sub commands * fix(init): admin user in config, test(init): verify functions * refactor: config, remove second commands * refactor: init steps * chore: fix link in readme * chore: numerate sql files * Update cmd/admin/initialise/sql/README.md Co-authored-by: Livio Amstutz <livio.a@gmail.com> * Update cmd/admin/initialise/sql/README.md Co-authored-by: Livio Amstutz <livio.a@gmail.com> * fix(init): remove unused index * user * fix database username in defaults.yaml Co-authored-by: Livio Amstutz <livio.a@gmail.com>
This commit is contained in:
parent
389eb4a27a
commit
4272ea6fe1
@ -3,5 +3,24 @@ package initialise
|
|||||||
import "github.com/caos/zitadel/internal/database"
|
import "github.com/caos/zitadel/internal/database"
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Database database.Config
|
Database database.Config
|
||||||
|
AdminUser database.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func adminConfig(config Config) database.Config {
|
||||||
|
adminConfig := config.Database
|
||||||
|
adminConfig.Username = config.AdminUser.Username
|
||||||
|
adminConfig.Password = config.AdminUser.Password
|
||||||
|
adminConfig.SSL.Cert = config.AdminUser.SSL.Cert
|
||||||
|
adminConfig.SSL.Key = config.AdminUser.SSL.Key
|
||||||
|
if config.AdminUser.SSL.RootCert != "" {
|
||||||
|
adminConfig.SSL.RootCert = config.AdminUser.SSL.RootCert
|
||||||
|
}
|
||||||
|
if config.AdminUser.SSL.Mode != "" {
|
||||||
|
adminConfig.SSL.Mode = config.AdminUser.SSL.Mode
|
||||||
|
}
|
||||||
|
//use default database because the zitadel database might not exist
|
||||||
|
adminConfig.Database = ""
|
||||||
|
|
||||||
|
return adminConfig
|
||||||
}
|
}
|
||||||
|
28
cmd/admin/initialise/helper.go
Normal file
28
cmd/admin/initialise/helper.go
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package initialise
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func exists(query string, args ...interface{}) func(*sql.DB) (exists bool, err error) {
|
||||||
|
return func(db *sql.DB) (exists bool, err error) {
|
||||||
|
row := db.QueryRow("SELECT EXISTS("+query+")", args...)
|
||||||
|
err = row.Scan(&exists)
|
||||||
|
return exists, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func exec(stmt string, args ...interface{}) func(*sql.DB) error {
|
||||||
|
return func(db *sql.DB) error {
|
||||||
|
_, err := db.Exec(stmt, args...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verify(db *sql.DB, checkExists func(*sql.DB) (bool, error), create func(*sql.DB) error) error {
|
||||||
|
exists, err := checkExists(db)
|
||||||
|
if exists || err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return create(db)
|
||||||
|
}
|
@ -13,20 +13,6 @@ import (
|
|||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
user string
|
|
||||||
password string
|
|
||||||
sslCert string
|
|
||||||
sslKey string
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
userFlag = "user"
|
|
||||||
passwordFlag = "password"
|
|
||||||
sslCertFlag = "ssl-cert"
|
|
||||||
sslKeyFlag = "ssl-key"
|
|
||||||
)
|
|
||||||
|
|
||||||
func New() *cobra.Command {
|
func New() *cobra.Command {
|
||||||
cmd := &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "init",
|
Use: "init",
|
||||||
@ -42,52 +28,39 @@ The user provided by flags needs priviledge to
|
|||||||
- grant all rights of the ZITADEL database to the user created if not yet set
|
- grant all rights of the ZITADEL database to the user created if not yet set
|
||||||
`,
|
`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
config := new(Config)
|
config := Config{}
|
||||||
if err := viper.Unmarshal(config); err != nil {
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return initialise(config, verifyUser, verifyDB, verifyGrant)
|
if err := initialise(config,
|
||||||
|
verifyUser(config.Database),
|
||||||
|
verifyDatabase(config.Database),
|
||||||
|
verifyGrant(config.Database),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return verifyZitadel(config.Database)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.PersistentFlags().StringVar(&password, passwordFlag, "", "password of the the provided user")
|
|
||||||
cmd.PersistentFlags().StringVar(&sslCert, sslCertFlag, "", "ssl cert from the provided user")
|
|
||||||
cmd.PersistentFlags().StringVar(&sslKey, sslKeyFlag, "", "ssl key from the provided user")
|
|
||||||
cmd.PersistentFlags().StringVar(&user, userFlag, "", "(required) the user to check if the database, user and grants exists and create if not")
|
|
||||||
cmd.MarkPersistentFlagRequired(userFlag)
|
|
||||||
|
|
||||||
cmd.AddCommand(newZitadel(), newDatabase(), newUser(), newGrant())
|
cmd.AddCommand(newZitadel(), newDatabase(), newUser(), newGrant())
|
||||||
|
|
||||||
return cmd
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
func adminConfig(config database.Config) database.Config {
|
func initialise(config Config, steps ...func(*sql.DB) error) error {
|
||||||
adminConfig := config
|
|
||||||
adminConfig.User = user
|
|
||||||
adminConfig.Password = password
|
|
||||||
adminConfig.SSL.Cert = sslCert
|
|
||||||
adminConfig.SSL.Key = sslKey
|
|
||||||
|
|
||||||
return adminConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
func initialise(config *Config, steps ...func(*sql.DB, database.Config) error) error {
|
|
||||||
logging.Info("initialization started")
|
logging.Info("initialization started")
|
||||||
|
|
||||||
db, err := database.Connect(adminConfig(config.Database))
|
db, err := database.Connect(adminConfig(config))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, step := range steps {
|
for _, step := range steps {
|
||||||
if err = step(db, config.Database); err != nil {
|
if err = step(db); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = db.Close(); err != nil {
|
return db.Close()
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return verifyZitadel(config.Database)
|
|
||||||
}
|
}
|
||||||
|
82
cmd/admin/initialise/init_test.go
Normal file
82
cmd/admin/initialise/init_test.go
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
package initialise
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
)
|
||||||
|
|
||||||
|
type db struct {
|
||||||
|
mock sqlmock.Sqlmock
|
||||||
|
db *sql.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareDB(t *testing.T, expectations ...expectation) db {
|
||||||
|
t.Helper()
|
||||||
|
client, mock, err := sqlmock.New()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create sql mock: %v", err)
|
||||||
|
}
|
||||||
|
for _, expectation := range expectations {
|
||||||
|
expectation(mock)
|
||||||
|
}
|
||||||
|
return db{
|
||||||
|
mock: mock,
|
||||||
|
db: client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type expectation func(m sqlmock.Sqlmock)
|
||||||
|
|
||||||
|
func expectExists(query string, value bool, args ...driver.Value) expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectQueryErr(query string, err error, args ...driver.Value) expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectExec(stmt string, err error, args ...driver.Value) expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectExec(regexp.QuoteMeta(stmt)).WithArgs(args...)
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
query.WillReturnResult(sqlmock.NewResult(1, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectBegin(err error) expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectBegin()
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectCommit(err error) expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectCommit()
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func expectRollback(err error) expectation {
|
||||||
|
return func(m sqlmock.Sqlmock) {
|
||||||
|
query := m.ExpectRollback()
|
||||||
|
if err != nil {
|
||||||
|
query.WillReturnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1
cmd/admin/initialise/sql/01_user.sql
Normal file
1
cmd/admin/initialise/sql/01_user.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
CREATE USER $1 WITH PASSWORD $2
|
2
cmd/admin/initialise/sql/02_database.sql
Normal file
2
cmd/admin/initialise/sql/02_database.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-- replace %[1]s with the name of the database
|
||||||
|
CREATE DATABASE %[1]s
|
3
cmd/admin/initialise/sql/03_grant_user.sql
Normal file
3
cmd/admin/initialise/sql/03_grant_user.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
-- replace the first %[1]s with the database
|
||||||
|
-- replace the second \%[2]s with the user
|
||||||
|
GRANT ALL ON DATABASE %[1]s TO %[2]s
|
1
cmd/admin/initialise/sql/04_eventstore.sql
Normal file
1
cmd/admin/initialise/sql/04_eventstore.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
CREATE SCHEMA eventstore
|
1
cmd/admin/initialise/sql/05_projections.sql
Normal file
1
cmd/admin/initialise/sql/05_projections.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
CREATE SCHEMA projections
|
@ -0,0 +1 @@
|
|||||||
|
SET experimental_enable_hash_sharded_indexes = on
|
24
cmd/admin/initialise/sql/07_events_table.sql
Normal file
24
cmd/admin/initialise/sql/07_events_table.sql
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
CREATE TABLE eventstore.events (
|
||||||
|
id UUID DEFAULT gen_random_uuid()
|
||||||
|
, event_type TEXT NOT NULL
|
||||||
|
, aggregate_type TEXT NOT NULL
|
||||||
|
, aggregate_id TEXT NOT NULL
|
||||||
|
, aggregate_version TEXT NOT NULL
|
||||||
|
, event_sequence BIGINT NOT NULL
|
||||||
|
, previous_aggregate_sequence BIGINT
|
||||||
|
, previous_aggregate_type_sequence INT8
|
||||||
|
, creation_date TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||||
|
, event_data JSONB
|
||||||
|
, editor_user TEXT NOT NULL
|
||||||
|
, editor_service TEXT NOT NULL
|
||||||
|
, resource_owner TEXT NOT NULL
|
||||||
|
|
||||||
|
, PRIMARY KEY (event_sequence DESC) USING HASH WITH BUCKET_COUNT = 10
|
||||||
|
, INDEX agg_type_agg_id (aggregate_type, aggregate_id)
|
||||||
|
, INDEX agg_type (aggregate_type)
|
||||||
|
, INDEX agg_type_seq (aggregate_type, event_sequence DESC)
|
||||||
|
STORING (id, event_type, aggregate_id, aggregate_version, previous_aggregate_sequence, creation_date, event_data, editor_user, editor_service, resource_owner, previous_aggregate_type_sequence)
|
||||||
|
, INDEX max_sequence (aggregate_type, aggregate_id, event_sequence DESC)
|
||||||
|
, CONSTRAINT previous_sequence_unique UNIQUE (previous_aggregate_sequence DESC)
|
||||||
|
, CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence)
|
||||||
|
)
|
14
cmd/admin/initialise/sql/README.md
Normal file
14
cmd/admin/initialise/sql/README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# SQL initialisation
|
||||||
|
|
||||||
|
The sql-files in this folder initialize the ZITADEL database and user. These objects need to exist before ZITADEL is able to set and start up.
|
||||||
|
|
||||||
|
## files
|
||||||
|
|
||||||
|
- 01_user.sql: create the user zitadel uses to connect to the database
|
||||||
|
- 02_database.sql: create the database for zitadel
|
||||||
|
- 03_grant_user.sql: grants the user created before to have full access to its database. The user needs full access to the database because zitadel makes ddl/dml on runtime
|
||||||
|
- 04_eventstore.sql: creates the schema needed for eventsourcing
|
||||||
|
- 05_projections.sql: creates the schema needed to read the data
|
||||||
|
- files 06_enable_hash_sharded_indexes.sql and 07_events_table.sql must run in the same session
|
||||||
|
- 06_enable_hash_sharded_indexes.sql enables the [hash sharded index](https://www.cockroachlabs.com/docs/stable/hash-sharded-indexes.html) feature for this session
|
||||||
|
- 07_events_table.sql creates the table for eventsourcing
|
@ -2,13 +2,21 @@ package initialise
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
|
||||||
"github.com/caos/zitadel/internal/database"
|
"github.com/caos/zitadel/internal/database"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
searchDatabase = "SELECT database_name FROM [show databases] WHERE database_name = $1"
|
||||||
|
|
||||||
|
//go:embed sql/02_database.sql
|
||||||
|
databaseStmt string
|
||||||
|
)
|
||||||
|
|
||||||
func newDatabase() *cobra.Command {
|
func newDatabase() *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "database",
|
Use: "database",
|
||||||
@ -24,31 +32,20 @@ The user provided by flags needs priviledge to
|
|||||||
- grant all rights of the ZITADEL database to the user created if not yet set
|
- grant all rights of the ZITADEL database to the user created if not yet set
|
||||||
`,
|
`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
config := new(Config)
|
config := Config{}
|
||||||
if err := viper.Unmarshal(config); err != nil {
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return initialise(config, verifyDB)
|
return initialise(config, verifyDatabase(config.Database))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyDB(db *sql.DB, config database.Config) error {
|
func verifyDatabase(config database.Config) func(*sql.DB) error {
|
||||||
logging.Info("verify database")
|
return func(db *sql.DB) error {
|
||||||
exists, err := existsDatabase(db, config)
|
return verify(db,
|
||||||
if exists || err != nil {
|
exists(searchDatabase, config.Database),
|
||||||
return err
|
exec(fmt.Sprintf(databaseStmt, config.Database)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return createDatabase(db, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func existsDatabase(db *sql.DB, config database.Config) (exists bool, err error) {
|
|
||||||
row := db.QueryRow("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", config.Database)
|
|
||||||
err = row.Scan(&exists)
|
|
||||||
return exists, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func createDatabase(db *sql.DB, config database.Config) error {
|
|
||||||
_, err := db.Exec("CREATE DATABASE " + config.Database)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
80
cmd/admin/initialise/verify_database_test.go
Normal file
80
cmd/admin/initialise/verify_database_test.go
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
package initialise
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_verifyDB(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
db db
|
||||||
|
config database.Config
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
targetErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exists fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t, expectQueryErr("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", sql.ErrConnDone, "zitadel")),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrConnDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "doesn't exists, create fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", false, "zitadel"),
|
||||||
|
expectExec("CREATE DATABASE zitadel", sql.ErrTxDone),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrTxDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "doesn't exists, create successful",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", false, "zitadel"),
|
||||||
|
expectExec("CREATE DATABASE zitadel", nil),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "already exists",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT database_name FROM [show databases] WHERE database_name = $1)", true, "zitadel"),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := verifyDatabase(tt.args.config)(tt.args.db.db); !errors.Is(err, tt.targetErr) {
|
||||||
|
t.Errorf("verifyDB() error = %v, want: %v", err, tt.targetErr)
|
||||||
|
}
|
||||||
|
if err := tt.args.db.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,8 @@ package initialise
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
"github.com/caos/zitadel/internal/database"
|
"github.com/caos/zitadel/internal/database"
|
||||||
@ -9,6 +11,12 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
searchGrant = "SELECT * FROM [SHOW GRANTS ON DATABASE %s] where grantee = $1 AND privilege_type = 'ALL'"
|
||||||
|
//go:embed sql/03_grant_user.sql
|
||||||
|
grantStmt string
|
||||||
|
)
|
||||||
|
|
||||||
func newGrant() *cobra.Command {
|
func newGrant() *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "grant",
|
Use: "grant",
|
||||||
@ -19,31 +27,21 @@ Prereqesits:
|
|||||||
- cockroachdb
|
- cockroachdb
|
||||||
`,
|
`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
config := new(Config)
|
config := Config{}
|
||||||
if err := viper.Unmarshal(config); err != nil {
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return initialise(config, verifyGrant)
|
return initialise(config, verifyGrant(config.Database))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyGrant(db *sql.DB, config database.Config) error {
|
func verifyGrant(config database.Config) func(*sql.DB) error {
|
||||||
logging.Info("verify grant")
|
return func(db *sql.DB) error {
|
||||||
exists, err := hasGrant(db, config)
|
logging.WithFields("user", config.Username).Info("verify grant")
|
||||||
if exists || err != nil {
|
return verify(db,
|
||||||
return err
|
exists(fmt.Sprintf(searchGrant, config.Database), config.Username),
|
||||||
|
exec(fmt.Sprintf(grantStmt, config.Database, config.Username)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return grant(db, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasGrant(db *sql.DB, config database.Config) (has bool, err error) {
|
|
||||||
row := db.QueryRow("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE "+config.Database+"] where grantee = $1 AND privilege_type = 'ALL')", config.User)
|
|
||||||
err = row.Scan(&has)
|
|
||||||
return has, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func grant(db *sql.DB, config database.Config) error {
|
|
||||||
_, err := db.Exec("GRANT ALL ON DATABASE " + config.Database + " TO " + config.User)
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
92
cmd/admin/initialise/verify_grant_test.go
Normal file
92
cmd/admin/initialise/verify_grant_test.go
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
package initialise
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_verifyGrant(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
db db
|
||||||
|
config database.Config
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
targetErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exists fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t, expectQueryErr("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", sql.ErrConnDone, "zitadel-user")),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrConnDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "doesn't exists, create fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", false, "zitadel-user"),
|
||||||
|
expectExec("GRANT ALL ON DATABASE zitadel TO zitadel-user", sql.ErrTxDone),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrTxDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "correct",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", false, "zitadel-user"),
|
||||||
|
expectExec("GRANT ALL ON DATABASE zitadel TO zitadel-user", nil),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "already exists",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT * FROM [SHOW GRANTS ON DATABASE zitadel] where grantee = $1 AND privilege_type = 'ALL'", true, "zitadel-user"),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := verifyGrant(tt.args.config)(tt.args.db.db); !errors.Is(err, tt.targetErr) {
|
||||||
|
t.Errorf("verifyGrant() error = %v, want: %v", err, tt.targetErr)
|
||||||
|
}
|
||||||
|
if err := tt.args.db.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package initialise
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
"github.com/caos/logging"
|
||||||
"github.com/caos/zitadel/internal/database"
|
"github.com/caos/zitadel/internal/database"
|
||||||
@ -9,6 +10,12 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
searchUser = "SELECT username FROM [show roles] WHERE username = $1"
|
||||||
|
//go:embed sql/01_user.sql
|
||||||
|
createUserStmt string
|
||||||
|
)
|
||||||
|
|
||||||
func newUser() *cobra.Command {
|
func newUser() *cobra.Command {
|
||||||
return &cobra.Command{
|
return &cobra.Command{
|
||||||
Use: "user",
|
Use: "user",
|
||||||
@ -24,31 +31,21 @@ The user provided by flags needs priviledge to
|
|||||||
- grant all rights of the ZITADEL database to the user created if not yet set
|
- grant all rights of the ZITADEL database to the user created if not yet set
|
||||||
`,
|
`,
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
config := new(Config)
|
config := Config{}
|
||||||
if err := viper.Unmarshal(config); err != nil {
|
if err := viper.Unmarshal(&config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return initialise(config, verifyUser)
|
return initialise(config, verifyUser(config.Database))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyUser(db *sql.DB, config database.Config) error {
|
func verifyUser(config database.Config) func(*sql.DB) error {
|
||||||
logging.Info("verify user")
|
return func(db *sql.DB) error {
|
||||||
exists, err := existsUser(db, config)
|
logging.WithFields("username", config.Username).Info("verify user")
|
||||||
if exists || err != nil {
|
return verify(db,
|
||||||
return err
|
exists(searchUser, config.Username),
|
||||||
|
exec(createUserStmt, config.Username, &sql.NullString{String: config.Password, Valid: config.Password != ""}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return createUser(db, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func existsUser(db *sql.DB, config database.Config) (exists bool, err error) {
|
|
||||||
row := db.QueryRow("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", config.User)
|
|
||||||
err = row.Scan(&exists)
|
|
||||||
return exists, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func createUser(db *sql.DB, config database.Config) error {
|
|
||||||
_, err := db.Exec("CREATE USER $1 WITH PASSWORD $2", config.User, &sql.NullString{String: config.Password, Valid: config.Password != ""})
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
109
cmd/admin/initialise/verify_user_test.go
Normal file
109
cmd/admin/initialise/verify_user_test.go
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package initialise
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_verifyUser(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
db db
|
||||||
|
config database.Config
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
targetErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "exists fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t, expectQueryErr("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", sql.ErrConnDone, "zitadel-user")),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrConnDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "doesn't exists, create fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", false, "zitadel-user"),
|
||||||
|
expectExec("CREATE USER $1 WITH PASSWORD $2", sql.ErrTxDone, "zitadel-user", nil),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrTxDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "correct without password",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", false, "zitadel-user"),
|
||||||
|
expectExec("CREATE USER $1 WITH PASSWORD $2", nil, "zitadel-user", nil),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "correct with password",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", false, "zitadel-user"),
|
||||||
|
expectExec("CREATE USER $1 WITH PASSWORD $2", nil, "zitadel-user", "password"),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
Password: "password",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "already exists",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectExists("SELECT EXISTS(SELECT username FROM [show roles] WHERE username = $1)", true, "zitadel-user"),
|
||||||
|
),
|
||||||
|
config: database.Config{
|
||||||
|
Database: "zitadel",
|
||||||
|
User: database.User{
|
||||||
|
Username: "zitadel-user",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := verifyUser(tt.args.config)(tt.args.db.db); !errors.Is(err, tt.targetErr) {
|
||||||
|
t.Errorf("verifyGrant() error = %v, want: %v", err, tt.targetErr)
|
||||||
|
}
|
||||||
|
if err := tt.args.db.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -2,8 +2,8 @@ package initialise
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
"github.com/caos/logging"
|
|
||||||
"github.com/caos/zitadel/internal/database"
|
"github.com/caos/zitadel/internal/database"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -12,7 +12,19 @@ import (
|
|||||||
const (
|
const (
|
||||||
eventstoreSchema = "eventstore"
|
eventstoreSchema = "eventstore"
|
||||||
projectionsSchema = "projections"
|
projectionsSchema = "projections"
|
||||||
eventsTable = "events"
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
searchEventsTable = "SELECT table_name FROM [SHOW TABLES] WHERE table_name = 'events'"
|
||||||
|
searchSchema = "SELECT schema_name FROM [SHOW SCHEMAS] WHERE schema_name = $1"
|
||||||
|
//go:embed sql/06_enable_hash_sharded_indexes.sql
|
||||||
|
enableHashShardedIdx string
|
||||||
|
//go:embed sql/07_events_table.sql
|
||||||
|
createEventsStmt string
|
||||||
|
//go:embed sql/05_projections.sql
|
||||||
|
createProjectionsStmt string
|
||||||
|
//go:embed sql/04_eventstore.sql
|
||||||
|
createEventstoreStmt string
|
||||||
)
|
)
|
||||||
|
|
||||||
func newZitadel() *cobra.Command {
|
func newZitadel() *cobra.Command {
|
||||||
@ -29,7 +41,7 @@ Prereqesits:
|
|||||||
if err := viper.Unmarshal(config); err != nil {
|
if err := viper.Unmarshal(config); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return initialise(config, verifyUser)
|
return verifyZitadel(config.Database)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -40,93 +52,32 @@ func verifyZitadel(config database.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := verifySchema(db, config, projectionsSchema); err != nil {
|
if err := verify(db, exists(searchSchema, projectionsSchema), exec(createProjectionsStmt)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := verifySchema(db, config, eventstoreSchema); err != nil {
|
if err := verify(db, exists(searchSchema, eventstoreSchema), exec(createEventstoreStmt)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := verifyEvents(db, config); err != nil {
|
if err := verify(db, exists(searchSchema, projectionsSchema), createEvents); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.Close()
|
return db.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifySchema(db *sql.DB, config database.Config, schema string) error {
|
func createEvents(db *sql.DB) error {
|
||||||
logging.WithFields("schema", schema).Info("verify schema")
|
|
||||||
exists, err := existsSchema(db, config, schema)
|
|
||||||
if exists || err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return createSchema(db, config, schema)
|
|
||||||
}
|
|
||||||
|
|
||||||
func existsSchema(db *sql.DB, config database.Config, schema string) (exists bool, err error) {
|
|
||||||
row := db.QueryRow("SELECT EXISTS(SELECT schema_name FROM [SHOW SCHEMAS] WHERE schema_name = $1)", schema)
|
|
||||||
err = row.Scan(&exists)
|
|
||||||
return exists, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func createSchema(db *sql.DB, config database.Config, schema string) error {
|
|
||||||
_, err := db.Exec("CREATE SCHEMA " + schema)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func verifyEvents(db *sql.DB, config database.Config) error {
|
|
||||||
logging.Info("verify events table")
|
|
||||||
|
|
||||||
exists, err := existsEvents(db, config)
|
|
||||||
if exists || err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return createEvents(db, config)
|
|
||||||
}
|
|
||||||
|
|
||||||
func existsEvents(db *sql.DB, config database.Config) (exists bool, err error) {
|
|
||||||
row := db.QueryRow("SELECT EXISTS(SELECT table_name FROM [SHOW TABLES] WHERE table_name = $1)", eventsTable)
|
|
||||||
err = row.Scan(&exists)
|
|
||||||
return exists, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func createEvents(db *sql.DB, config database.Config) error {
|
|
||||||
tx, err := db.Begin()
|
tx, err := db.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err = tx.Exec("SET experimental_enable_hash_sharded_indexes = on"); err != nil {
|
if _, err = tx.Exec(enableHashShardedIdx); err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt := `CREATE TABLE eventstore.events (
|
if _, err = tx.Exec(createEventsStmt); err != nil {
|
||||||
id UUID DEFAULT gen_random_uuid()
|
|
||||||
, event_type TEXT NOT NULL
|
|
||||||
, aggregate_type TEXT NOT NULL
|
|
||||||
, aggregate_id TEXT NOT NULL
|
|
||||||
, aggregate_version TEXT NOT NULL
|
|
||||||
, event_sequence BIGINT NOT NULL
|
|
||||||
, previous_aggregate_sequence BIGINT
|
|
||||||
, previous_aggregate_type_sequence INT8
|
|
||||||
, creation_date TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
||||||
, event_data JSONB
|
|
||||||
, editor_user TEXT NOT NULL
|
|
||||||
, editor_service TEXT NOT NULL
|
|
||||||
, resource_owner TEXT NOT NULL
|
|
||||||
|
|
||||||
, PRIMARY KEY (event_sequence DESC) USING HASH WITH BUCKET_COUNT = 10
|
|
||||||
, INDEX agg_type_agg_id (aggregate_type, aggregate_id)
|
|
||||||
, INDEX agg_type (aggregate_type)
|
|
||||||
, INDEX agg_type_seq (aggregate_type, event_sequence DESC)
|
|
||||||
STORING (id, event_type, aggregate_id, aggregate_version, previous_aggregate_sequence, creation_date, event_data, editor_user, editor_service, resource_owner, previous_aggregate_type_sequence)
|
|
||||||
, INDEX changes_idx (aggregate_type, aggregate_id, creation_date) USING HASH WITH BUCKET_COUNT = 10
|
|
||||||
, INDEX max_sequence (aggregate_type, aggregate_id, event_sequence DESC)
|
|
||||||
, CONSTRAINT previous_sequence_unique UNIQUE (previous_aggregate_sequence DESC)
|
|
||||||
, CONSTRAINT prev_agg_type_seq_unique UNIQUE(previous_aggregate_type_sequence)
|
|
||||||
)`
|
|
||||||
if _, err = tx.Exec(stmt); err != nil {
|
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
73
cmd/admin/initialise/verify_zitadel_test.go
Normal file
73
cmd/admin/initialise/verify_zitadel_test.go
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package initialise
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_verifyEvents(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
db db
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
targetErr error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "unable to begin",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectBegin(sql.ErrConnDone),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrConnDone,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hash sharded indexes fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec("SET experimental_enable_hash_sharded_indexes = on", sql.ErrNoRows),
|
||||||
|
expectRollback(nil),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrNoRows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create table fails",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec("SET experimental_enable_hash_sharded_indexes = on", nil),
|
||||||
|
expectExec(createEventsStmt, sql.ErrNoRows),
|
||||||
|
expectRollback(nil),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
targetErr: sql.ErrNoRows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "correct",
|
||||||
|
args: args{
|
||||||
|
db: prepareDB(t,
|
||||||
|
expectBegin(nil),
|
||||||
|
expectExec("SET experimental_enable_hash_sharded_indexes = on", nil),
|
||||||
|
expectExec(createEventsStmt, nil),
|
||||||
|
expectCommit(nil),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
targetErr: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if err := createEvents(tt.args.db.db); !errors.Is(err, tt.targetErr) {
|
||||||
|
t.Errorf("createEvents() error = %v, want: %v", err, tt.targetErr)
|
||||||
|
}
|
||||||
|
if err := tt.args.db.mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -11,18 +11,22 @@ ExternalSecure: true
|
|||||||
Database:
|
Database:
|
||||||
Host: localhost
|
Host: localhost
|
||||||
Port: 26257
|
Port: 26257
|
||||||
User: zitadel
|
|
||||||
Database: zitadel
|
Database: zitadel
|
||||||
Password: ""
|
|
||||||
MaxOpenConns: 20
|
MaxOpenConns: 20
|
||||||
MaxConnLifetime: 30m
|
MaxConnLifetime: 30m
|
||||||
MaxConnIdleTime: 30m
|
MaxConnIdleTime: 30m
|
||||||
Options: ""
|
Options: ""
|
||||||
SSL:
|
User:
|
||||||
Mode: diabled
|
Username: zitadel
|
||||||
RootCert: ""
|
Password: ""
|
||||||
Cert: ""
|
SSL:
|
||||||
Key: ""
|
Mode: diabled
|
||||||
|
RootCert: ""
|
||||||
|
Cert: ""
|
||||||
|
Key: ""
|
||||||
|
|
||||||
|
AdminUser:
|
||||||
|
Username: root
|
||||||
|
|
||||||
Projections:
|
Projections:
|
||||||
Config:
|
Config:
|
||||||
|
@ -14,19 +14,23 @@ const (
|
|||||||
type Config struct {
|
type Config struct {
|
||||||
Host string
|
Host string
|
||||||
Port string
|
Port string
|
||||||
User string
|
|
||||||
Password string
|
|
||||||
Database string
|
Database string
|
||||||
SSL SSL
|
|
||||||
MaxOpenConns uint32
|
MaxOpenConns uint32
|
||||||
MaxConnLifetime time.Duration
|
MaxConnLifetime time.Duration
|
||||||
MaxConnIdleTime time.Duration
|
MaxConnIdleTime time.Duration
|
||||||
|
User
|
||||||
|
|
||||||
//Additional options to be appended as options=<Options>
|
//Additional options to be appended as options=<Options>
|
||||||
//The value will be taken as is. Multiple options are space separated.
|
//The value will be taken as is. Multiple options are space separated.
|
||||||
Options string
|
Options string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
Username string
|
||||||
|
Password string
|
||||||
|
SSL SSL
|
||||||
|
}
|
||||||
|
|
||||||
type SSL struct {
|
type SSL struct {
|
||||||
// type of connection security
|
// type of connection security
|
||||||
Mode string
|
Mode string
|
||||||
@ -57,7 +61,7 @@ func (c Config) String() string {
|
|||||||
fields := []string{
|
fields := []string{
|
||||||
"host=" + c.Host,
|
"host=" + c.Host,
|
||||||
"port=" + c.Port,
|
"port=" + c.Port,
|
||||||
"user=" + c.User,
|
"user=" + c.Username,
|
||||||
"dbname=" + c.Database,
|
"dbname=" + c.Database,
|
||||||
"application_name=zitadel",
|
"application_name=zitadel",
|
||||||
"sslmode=" + c.SSL.Mode,
|
"sslmode=" + c.SSL.Mode,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user