diff --git a/internal/config/database.go b/internal/config/database.go index 9b4137fc..b9b6d90f 100644 --- a/internal/config/database.go +++ b/internal/config/database.go @@ -50,12 +50,12 @@ type DBFiler interface { } type DBHasUser interface { - UserEnvs() kubernetes.ConfigLookups + UserEnvs(conf Global) kubernetes.ConfigLookups UserDefault() string } type DBHasPort interface { - PortEnvs() kubernetes.ConfigLookups + PortEnvs(conf Global) kubernetes.ConfigLookups PortDefault() uint16 } @@ -64,7 +64,7 @@ type DBHasPassword interface { } type DBHasDatabase interface { - DatabaseEnvs() kubernetes.ConfigLookups + DatabaseEnvs(conf Global) kubernetes.ConfigLookups } type DBDatabaseLister interface { diff --git a/internal/database/mariadb/mariadb.go b/internal/database/mariadb/mariadb.go index 1ffafb34..d14e6bf8 100644 --- a/internal/database/mariadb/mariadb.go +++ b/internal/database/mariadb/mariadb.go @@ -36,7 +36,7 @@ func (MariaDB) Aliases() []string { return []string{"maria", "mysql"} } func (MariaDB) Priority() uint8 { return 255 } -func (MariaDB) PortEnvs() kubernetes.ConfigLookups { +func (MariaDB) PortEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"MARIADB_PORT_NUMBER", "MYSQL_PORT_NUMBER"}} } @@ -44,7 +44,7 @@ func (MariaDB) PortDefault() uint16 { return 3306 } -func (MariaDB) DatabaseEnvs() kubernetes.ConfigLookups { +func (MariaDB) DatabaseEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"MARIADB_DATABASE", "MYSQL_DATABASE"}} } @@ -52,7 +52,7 @@ func (MariaDB) DatabaseListQuery() string { return "show databases" } func (MariaDB) TableListQuery() string { return "show tables" } -func (MariaDB) UserEnvs() kubernetes.ConfigLookups { +func (MariaDB) UserEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"MARIADB_USER", "MYSQL_USER"}} } diff --git a/internal/database/meilisearch/meilisearch.go b/internal/database/meilisearch/meilisearch.go index 35483df0..21896547 100644 --- a/internal/database/meilisearch/meilisearch.go +++ b/internal/database/meilisearch/meilisearch.go @@ -25,7 +25,9 @@ func (Meilisearch) Name() string { return "meilisearch" } func (Meilisearch) PrettyName() string { return "Meilisearch" } -func (Meilisearch) PortEnvs() kubernetes.ConfigLookups { return kubernetes.ConfigLookups{} } +func (Meilisearch) PortEnvs(_ config.Global) kubernetes.ConfigLookups { + return kubernetes.ConfigLookups{} +} func (Meilisearch) PortDefault() uint16 { return 7700 } diff --git a/internal/database/mongodb/mongodb.go b/internal/database/mongodb/mongodb.go index d7539593..e8551ad0 100644 --- a/internal/database/mongodb/mongodb.go +++ b/internal/database/mongodb/mongodb.go @@ -36,13 +36,13 @@ func (MongoDB) Aliases() []string { return []string{"mongo"} } func (MongoDB) Priority() uint8 { return 255 } -func (MongoDB) PortEnvs() kubernetes.ConfigLookups { +func (MongoDB) PortEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"MONGODB_PORT_NUMBER"}} } func (MongoDB) PortDefault() uint16 { return 27017 } -func (MongoDB) DatabaseEnvs() kubernetes.ConfigLookups { +func (MongoDB) DatabaseEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"MONGODB_EXTRA_DATABASES"}} } @@ -54,7 +54,7 @@ func (MongoDB) TableListQuery() string { return "db.getCollectionNames().forEach(function(collection){ print(collection) })" } -func (MongoDB) UserEnvs() kubernetes.ConfigLookups { +func (MongoDB) UserEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"MONGODB_EXTRA_USERNAMES", "MONGODB_ROOT_USER"}} } diff --git a/internal/database/postgres/postgres.go b/internal/database/postgres/postgres.go index 0d62fae2..acff344b 100644 --- a/internal/database/postgres/postgres.go +++ b/internal/database/postgres/postgres.go @@ -47,19 +47,31 @@ func (Postgres) Aliases() []string { return []string{"postgresql", "psql", "pg"} func (Postgres) Priority() uint8 { return 255 } -func (Postgres) PortEnvs() kubernetes.ConfigLookups { +func (db Postgres) PortEnvs(conf config.Global) kubernetes.ConfigLookups { + if secret := db.cnpgSecretName(conf); secret != "" { + return kubernetes.ConfigLookups{kubernetes.LookupNamedSecret{ + Name: secret, + Key: "port", + }} + } + return kubernetes.ConfigLookups{ kubernetes.LookupEnv{"POSTGRESQL_PORT_NUMBER"}, - kubernetes.LookupVolumeSecret{Name: "app-secret", Key: "port"}, } } func (Postgres) PortDefault() uint16 { return 5432 } -func (Postgres) DatabaseEnvs() kubernetes.ConfigLookups { +func (db Postgres) DatabaseEnvs(conf config.Global) kubernetes.ConfigLookups { + if secret := db.cnpgSecretName(conf); secret != "" { + return kubernetes.ConfigLookups{kubernetes.LookupNamedSecret{ + Name: secret, + Key: "dbname", + }} + } + return kubernetes.ConfigLookups{ kubernetes.LookupEnv{"POSTGRES_DATABASE", "POSTGRES_DB"}, - kubernetes.LookupVolumeSecret{Name: "app-secret", Key: "dbname"}, } } @@ -71,10 +83,16 @@ func (Postgres) TableListQuery() string { return "SELECT table_name FROM information_schema.tables WHERE table_schema='public' AND table_type='BASE TABLE'" } -func (Postgres) UserEnvs() kubernetes.ConfigLookups { +func (db Postgres) UserEnvs(conf config.Global) kubernetes.ConfigLookups { + if secret := db.cnpgSecretName(conf); secret != "" { + return kubernetes.ConfigLookups{kubernetes.LookupNamedSecret{ + Name: secret, + Key: "username", + }} + } + return kubernetes.ConfigLookups{ kubernetes.LookupEnv{"POSTGRES_USER", "PGPOOL_POSTGRES_USERNAME", "PGUSER_SUPERUSER"}, - kubernetes.LookupVolumeSecret{Name: "app-secret", Key: "username"}, } } @@ -176,17 +194,20 @@ func (db Postgres) FilterPods(ctx context.Context, client kubernetes.KubeClient, return preferred, nil } -func (db Postgres) PasswordEnvs(c config.Global) kubernetes.ConfigLookups { +func (db Postgres) PasswordEnvs(conf config.Global) kubernetes.ConfigLookups { + if secret := db.cnpgSecretName(conf); secret != "" { + return kubernetes.ConfigLookups{kubernetes.LookupNamedSecret{ + Name: secret, + Key: "password", + }} + } + var searchEnvs kubernetes.LookupEnv - searchUser := kubernetes.LookupVolumeSecret{Key: "password"} - if c.Username == db.UserDefault() { + if conf.Username == db.UserDefault() { searchEnvs = append(searchEnvs, "POSTGRES_POSTGRES_PASSWORD") - searchUser.Name = "superuser-secret" - } else { - searchUser.Name = "app-secret" } searchEnvs = append(searchEnvs, "POSTGRES_PASSWORD", "PGPOOL_POSTGRES_PASSWORD", "PGPASSWORD_SUPERUSER") - return kubernetes.ConfigLookups{searchEnvs, searchUser} + return kubernetes.ConfigLookups{searchEnvs} } func (Postgres) ExecCommand(conf config.Exec) *command.Builder { @@ -330,3 +351,13 @@ func (Postgres) cnpgQuery() filter.Filter { func (Postgres) zalandoQuery() filter.Filter { return filter.Label{Name: "application", Value: "spilo"} } + +func (db Postgres) cnpgSecretName(conf config.Global) string { + if cluster, ok := conf.DBPod.Labels["cnpg.io/cluster"]; ok { + if conf.Username == db.UserDefault() { + return cluster + "-superuser" + } + return cluster + "-app" + } + return "" +} diff --git a/internal/database/postgres/postgres_test.go b/internal/database/postgres/postgres_test.go index c3eee936..67d5a370 100644 --- a/internal/database/postgres/postgres_test.go +++ b/internal/database/postgres/postgres_test.go @@ -14,6 +14,17 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +func newCNPGPod() v1.Pod { + return v1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "postgresql-1", + Labels: map[string]string{ + "cnpg.io/cluster": "postgresql", + }, + }, + } +} + func TestPostgres_DumpCommand(t *testing.T) { t.Parallel() type args struct { @@ -171,6 +182,7 @@ func TestPostgres_FilterPods(t *testing.T) { func TestPostgres_PasswordEnvs(t *testing.T) { t.Parallel() + type args struct { c config.Global } @@ -185,7 +197,6 @@ func TestPostgres_PasswordEnvs(t *testing.T) { "PGPOOL_POSTGRES_PASSWORD", "PGPASSWORD_SUPERUSER", }, - kubernetes.LookupVolumeSecret{Name: "app-secret", Key: "password"}, }}, {"postgres", args{config.Global{Username: "postgres"}}, kubernetes.ConfigLookups{ kubernetes.LookupEnv{ @@ -194,7 +205,12 @@ func TestPostgres_PasswordEnvs(t *testing.T) { "PGPOOL_POSTGRES_PASSWORD", "PGPASSWORD_SUPERUSER", }, - kubernetes.LookupVolumeSecret{Name: "superuser-secret", Key: "password"}, + }}, + {"cnpg", args{config.Global{DBPod: newCNPGPod()}}, kubernetes.ConfigLookups{ + kubernetes.LookupNamedSecret{ + Name: "postgresql-app", + Key: "password", + }, }}, } for _, tt := range tests { @@ -307,3 +323,99 @@ func TestPostgres_quoteParam(t *testing.T) { }) } } + +func TestPostgres_cnpgSecretName(t *testing.T) { + type args struct { + conf config.Global + } + tests := []struct { + name string + args args + want string + }{ + {"default", args{config.Global{DBPod: newCNPGPod()}}, "postgresql-app"}, + {"postgres", args{config.Global{Username: "postgres", DBPod: newCNPGPod()}}, "postgresql-superuser"}, + {"other", args{}, ""}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := Postgres{} + assert.Equal(t, tt.want, db.cnpgSecretName(tt.args.conf)) + }) + } +} + +func TestPostgres_PortEnvs(t *testing.T) { + type args struct { + conf config.Global + } + tests := []struct { + name string + args args + want kubernetes.ConfigLookups + }{ + {"default", args{}, kubernetes.ConfigLookups{kubernetes.LookupEnv{"POSTGRESQL_PORT_NUMBER"}}}, + {"cnpg", args{config.Global{DBPod: newCNPGPod()}}, kubernetes.ConfigLookups{ + kubernetes.LookupNamedSecret{ + Name: "postgresql-app", + Key: "port", + }, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := Postgres{} + assert.Equal(t, tt.want, db.PortEnvs(tt.args.conf)) + }) + } +} + +func TestPostgres_DatabaseEnvs(t *testing.T) { + type args struct { + conf config.Global + } + tests := []struct { + name string + args args + want kubernetes.ConfigLookups + }{ + {"default", args{}, kubernetes.ConfigLookups{kubernetes.LookupEnv{"POSTGRES_DATABASE", "POSTGRES_DB"}}}, + {"cnpg", args{config.Global{DBPod: newCNPGPod()}}, kubernetes.ConfigLookups{ + kubernetes.LookupNamedSecret{ + Name: "postgresql-app", + Key: "dbname", + }, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := Postgres{} + assert.Equal(t, tt.want, db.DatabaseEnvs(tt.args.conf)) + }) + } +} + +func TestPostgres_UserEnvs(t *testing.T) { + type args struct { + conf config.Global + } + tests := []struct { + name string + args args + want kubernetes.ConfigLookups + }{ + {"default", args{}, kubernetes.ConfigLookups{kubernetes.LookupEnv{"POSTGRES_USER", "PGPOOL_POSTGRES_USERNAME", "PGUSER_SUPERUSER"}}}, + {"cnpg", args{config.Global{DBPod: newCNPGPod()}}, kubernetes.ConfigLookups{ + kubernetes.LookupNamedSecret{ + Name: "postgresql-app", + Key: "username", + }, + }}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + db := Postgres{} + assert.Equal(t, tt.want, db.UserEnvs(tt.args.conf)) + }) + } +} diff --git a/internal/database/redis/redis.go b/internal/database/redis/redis.go index 65c950d0..2f12d634 100644 --- a/internal/database/redis/redis.go +++ b/internal/database/redis/redis.go @@ -29,13 +29,13 @@ func (Redis) Name() string { return "redis" } func (Redis) PrettyName() string { return "Redis" } -func (Redis) PortEnvs() kubernetes.ConfigLookups { +func (Redis) PortEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"REDIS_PORT"}} } func (Redis) PortDefault() uint16 { return 6379 } -func (Redis) DatabaseEnvs() kubernetes.ConfigLookups { +func (Redis) DatabaseEnvs(_ config.Global) kubernetes.ConfigLookups { return kubernetes.ConfigLookups{kubernetes.LookupEnv{"REDIS_DB"}} } diff --git a/internal/kubernetes/config_lookup.go b/internal/kubernetes/config_lookup.go index 9f5c83bd..e4db644a 100644 --- a/internal/kubernetes/config_lookup.go +++ b/internal/kubernetes/config_lookup.go @@ -110,33 +110,25 @@ func (e LookupEnv) GetValue(ctx context.Context, client KubeClient, pod corev1.P return "", fmt.Errorf("%w: %s", ErrEnvNoExist, strings.Join(e, ", ")) } -type LookupVolumeSecret struct { +type LookupNamedSecret struct { Name string Key string } -var ErrVolumeNoExist = errors.New("volume does not exist") - -func (f LookupVolumeSecret) GetValue(ctx context.Context, client KubeClient, pod corev1.Pod) (string, error) { +func (f LookupNamedSecret) GetValue(ctx context.Context, client KubeClient, _ corev1.Pod) (string, error) { if f.Name == "" || f.Key == "" { return "", ErrNoEnvNames } - for _, volume := range pod.Spec.Volumes { - if volume.Name == f.Name && volume.Secret != nil { - secret, err := client.Secrets().Get(ctx, volume.Secret.SecretName, v1meta.GetOptions{}) - if err != nil { - return "", err - } - - if value, ok := secret.Data[f.Key]; ok { - return string(value), nil - } - return "", fmt.Errorf("%w: %s", ErrSecretDoesNotHaveKey, f.Key) - } + secret, err := client.Secrets().Get(ctx, f.Name, v1meta.GetOptions{}) + if err != nil { + return "", err } - return "", fmt.Errorf("%w: %s", ErrVolumeNoExist, f.Name) + if value, ok := secret.Data[f.Key]; ok { + return string(value), nil + } + return "", fmt.Errorf("%w: %s", ErrSecretDoesNotHaveKey, f.Key) } type LookupNop struct{} diff --git a/internal/util/cmd_setup.go b/internal/util/cmd_setup.go index 285b6439..eb8ff1df 100644 --- a/internal/util/cmd_setup.go +++ b/internal/util/cmd_setup.go @@ -178,7 +178,7 @@ func DefaultSetup(cmd *cobra.Command, conf *config.Global, opts SetupOptions) er } if db, ok := conf.Dialect.(config.DBHasPort); ok && conf.Port == 0 { - port, err := db.PortEnvs().Search(ctx, conf.Client, conf.DBPod) + port, err := db.PortEnvs(*conf).Search(ctx, conf.Client, conf.DBPod) if err != nil { slog.Debug("Could not detect port from pod env") } else { @@ -203,7 +203,7 @@ func DefaultSetup(cmd *cobra.Command, conf *config.Global, opts SetupOptions) er } if db, ok := conf.Dialect.(config.DBHasDatabase); ok && conf.Database == "" { - conf.Database, err = db.DatabaseEnvs().Search(ctx, conf.Client, conf.DBPod) + conf.Database, err = db.DatabaseEnvs(*conf).Search(ctx, conf.Client, conf.DBPod) if err != nil { slog.Debug("Could not detect database from pod env", "error", err) } else { @@ -218,7 +218,7 @@ func DefaultSetup(cmd *cobra.Command, conf *config.Global, opts SetupOptions) er } if db, ok := conf.Dialect.(config.DBHasUser); ok && conf.Username == "" { - conf.Username, err = db.UserEnvs().Search(ctx, conf.Client, conf.DBPod) + conf.Username, err = db.UserEnvs(*conf).Search(ctx, conf.Client, conf.DBPod) if err != nil { conf.Username = db.UserDefault() slog.Debug("Could not detect user from pod env, using default", "error", err, "user", conf.Username)