From 415a7fdc4da8e4b4ba90cb84c39412eb12ce1798 Mon Sep 17 00:00:00 2001 From: Oleksii Kliukin Date: Fri, 3 Nov 2017 15:58:36 +0100 Subject: [PATCH] Allow global configuration options for API roles. Add options to the PgUser structure, potentially allowing to set per-role options in the cluster definition as well. Introduce api_roles_configuration operator option with the default of log_statement=all --- manifests/configmap.yaml | 1 + pkg/cluster/cluster.go | 7 ++++++- pkg/cluster/sync.go | 6 +++++- pkg/spec/types.go | 2 ++ pkg/util/config/config.go | 13 +++++++------ pkg/util/users/users.go | 31 +++++++++++++++++++++++++++++++ 6 files changed, 52 insertions(+), 8 deletions(-) diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index e265c1907..0c9b2a65b 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -39,3 +39,4 @@ data: pod_terminate_grace_period: 5m pdb_name_format: "postgres-{cluster}-pdb" eol_node_label: "eol:true" + api_roles_configuration: "log_statement:all" diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 6c39e4c01..38579d3c2 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -645,7 +645,12 @@ func (c *Cluster) initHumanUsers() error { } } - c.pgUsers[username] = spec.PgUser{Name: username, Flags: flags, MemberOf: memberOf} + c.pgUsers[username] = spec.PgUser{ + Name: username, + Flags: flags, + MemberOf: memberOf, + Parameters: c.OpConfig.APIRolesParameters, + } } return nil diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index 43b039888..8114b6442 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -359,7 +359,11 @@ func (c *Cluster) syncRoles(readFromDatabase bool) error { if err != nil { return fmt.Errorf("could not init db connection: %v", err) } - defer c.closeDbConn() + defer func() { + if err := c.closeDbConn(); err != nil { + c.logger.Errorf("could not close db connection: %v", err) + } + }() if readFromDatabase { for _, u := range c.pgUsers { diff --git a/pkg/spec/types.go b/pkg/spec/types.go index d12bc084f..a350633f6 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -44,6 +44,7 @@ type syncUserOperation int const ( PGSyncUserAdd = iota PGsyncUserAlter + PGSyncAlterSet // handle ALTER ROLE SET parameter = value ) // PodEvent describes the event for a single Pod @@ -61,6 +62,7 @@ type PgUser struct { Password string Flags []string MemberOf []string + Parameters map[string]string } // PgUserMap maps user names to the definitions. diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index b01494142..4910017ac 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -65,12 +65,13 @@ type Config struct { TeamAdminRole string `name:"team_admin_role" default:"admin"` EnableLoadBalancer bool `name:"enable_load_balancer" default:"true"` MasterDNSNameFormat stringTemplate `name:"master_dns_name_format" default:"{cluster}.{team}.{hostedzone}"` - ReplicaDNSNameFormat stringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"` - PDBNameFormat stringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` - Workers uint32 `name:"workers" default:"4"` - APIPort int `name:"api_port" default:"8080"` - RingLogLines int `name:"ring_log_lines" default:"100"` - ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"` + ReplicaDNSNameFormat stringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"` + PDBNameFormat stringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"` + Workers uint32 `name:"workers" default:"4"` + APIPort int `name:"api_port" default:"8080"` + RingLogLines int `name:"ring_log_lines" default:"100"` + ClusterHistoryEntries int `name:"cluster_history_entries" default:"1000"` + APIRolesParameters map[string]string `name:"api_roles_configuration" default:"log_statement:all"` PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` } diff --git a/pkg/util/users/users.go b/pkg/util/users/users.go index 2bf29a0b5..456b13a1a 100644 --- a/pkg/util/users/users.go +++ b/pkg/util/users/users.go @@ -7,11 +7,14 @@ import ( "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util" + "reflect" ) const ( createUserSQL = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;` alterUserSQL = `ALTER ROLE "%s" %s` + alterRoleResetAllSQL = `ALTER ROLE "%s" RESET ALL` + alterRoleSetSQL = `ALTER ROLE "%s" SET "%s" TO "%s"` grantToUserSQL = `GRANT %s TO "%s"` doBlockStmt = `SET LOCAL synchronous_commit = 'local'; DO $$ BEGIN %s; END;$$;` passwordTemplate = "ENCRYPTED PASSWORD '%s'" @@ -34,6 +37,9 @@ func (s DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserMap, dbUser, exists := dbUsers[name] if !exists { reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser}) + if len(newUser.Parameters) > 0 { + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncAlterSet, User: newUser}) + } } else { r := spec.PgSyncUserRequest{} newMD5Password := util.PGUserPassword(newUser) @@ -54,6 +60,9 @@ func (s DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserMap, r.User.Name = newUser.Name reqs = append(reqs, r) } + if !reflect.DeepEqual(dbUser.Parameters, newUser.Parameters) { + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncAlterSet, User: newUser}) + } } } @@ -72,6 +81,10 @@ func (s DefaultUserSyncStrategy) ExecuteSyncRequests(reqs []spec.PgSyncUserReque if err := s.alterPgUser(r.User, db); err != nil { return fmt.Errorf("could not alter user %q: %v", r.User.Name, err) } + case spec.PGSyncAlterSet: + if err := s.alterPgUserSet(r.User, db); err != nil { + return fmt.Errorf("could not set custom user %q parameters: %v", r.User.Name, err) + } default: return fmt.Errorf("unrecognized operation: %v", r.Kind) } @@ -79,6 +92,15 @@ func (s DefaultUserSyncStrategy) ExecuteSyncRequests(reqs []spec.PgSyncUserReque } return nil } +func (strategy DefaultUserSyncStrategy) alterPgUserSet(user spec.PgUser, db *sql.DB) (err error) { + queries := produceAlterRoleSetStmts(user) + query := fmt.Sprintf(doBlockStmt, strings.Join(queries, ";")) + if _, err = db.Query(query); err != nil { + err = fmt.Errorf("dB error: %v, query: %q", err, query) + return + } + return +} func (s DefaultUserSyncStrategy) createPgUser(user spec.PgUser, db *sql.DB) (err error) { var userFlags []string @@ -148,6 +170,15 @@ func produceAlterStmt(user spec.PgUser) string { return fmt.Sprintf(alterUserSQL, user.Name, strings.Join(result, " ")) } +func produceAlterRoleSetStmts(user spec.PgUser) []string { + result := make([]string, 1) + result = append(result, fmt.Sprintf(alterRoleResetAllSQL, user.Name)) + for key, value := range(user.Parameters) { + result = append(result, fmt.Sprintf(alterRoleSetSQL, user.Name, key, value)) + } + return result +} + func produceGrantStmt(user spec.PgUser) string { // GRANT ROLE "foo", "bar" TO baz return fmt.Sprintf(grantToUserSQL, quoteMemberList(user), user.Name)