fix team member deprecation (#2072)
This commit is contained in:
parent
84fe38a069
commit
ce8b009c66
|
|
@ -17,6 +17,8 @@ jobs:
|
|||
go-version: "^1.17.4"
|
||||
- name: Make dependencies
|
||||
run: make deps mocks
|
||||
- name: Code generation
|
||||
run: make codegen
|
||||
- name: Compile
|
||||
run: make linux
|
||||
- name: Run unit tests
|
||||
|
|
|
|||
|
|
@ -250,6 +250,8 @@ class EndToEndTestCase(unittest.TestCase):
|
|||
}
|
||||
k8s.update_config(enable_postgres_team_crd)
|
||||
|
||||
# add team and member to custom-team-membership
|
||||
# contains already elephant user
|
||||
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
||||
'acid.zalan.do', 'v1', 'default',
|
||||
'postgresteams', 'custom-team-membership',
|
||||
|
|
@ -300,6 +302,13 @@ class EndToEndTestCase(unittest.TestCase):
|
|||
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2,
|
||||
"Database role of replaced member in PostgresTeam not renamed", 10, 5)
|
||||
|
||||
# create fake deletion user so operator fails renaming
|
||||
# but altering role to NOLOGIN will succeed
|
||||
create_fake_deletion_user = """
|
||||
CREATE USER tester_delete_me NOLOGIN;
|
||||
"""
|
||||
self.query_database(leader.metadata.name, "postgres", create_fake_deletion_user)
|
||||
|
||||
# re-add additional member and check if the role is renamed back
|
||||
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
||||
'acid.zalan.do', 'v1', 'default',
|
||||
|
|
@ -317,11 +326,44 @@ class EndToEndTestCase(unittest.TestCase):
|
|||
user_query = """
|
||||
SELECT rolname
|
||||
FROM pg_catalog.pg_roles
|
||||
WHERE (rolname = 'kind' AND rolcanlogin)
|
||||
OR (rolname = 'tester_delete_me' AND NOT rolcanlogin);
|
||||
WHERE rolname = 'kind' AND rolcanlogin;
|
||||
"""
|
||||
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 1,
|
||||
"Database role of recreated member in PostgresTeam not renamed back to original name", 10, 5)
|
||||
|
||||
user_query = """
|
||||
SELECT rolname
|
||||
FROM pg_catalog.pg_roles
|
||||
WHERE rolname IN ('tester','tester_delete_me') AND NOT rolcanlogin;
|
||||
"""
|
||||
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2,
|
||||
"Database role of recreated member in PostgresTeam not renamed back to original name", 10, 5)
|
||||
"Database role of replaced member in PostgresTeam not denied from login", 10, 5)
|
||||
|
||||
# re-add other additional member, operator should grant LOGIN back to tester
|
||||
# but nothing happens to deleted role
|
||||
k8s.api.custom_objects_api.patch_namespaced_custom_object(
|
||||
'acid.zalan.do', 'v1', 'default',
|
||||
'postgresteams', 'custom-team-membership',
|
||||
{
|
||||
'spec': {
|
||||
'additionalMembers': {
|
||||
'e2e': [
|
||||
'kind',
|
||||
'tester'
|
||||
]
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
user_query = """
|
||||
SELECT rolname
|
||||
FROM pg_catalog.pg_roles
|
||||
WHERE (rolname IN ('tester', 'kind')
|
||||
AND rolcanlogin)
|
||||
OR (rolname = 'tester_delete_me' AND NOT rolcanlogin);
|
||||
"""
|
||||
self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 3,
|
||||
"Database role of deleted member in PostgresTeam not removed when recreated manually", 10, 5)
|
||||
|
||||
# revert config change
|
||||
revert_resync = {
|
||||
|
|
@ -1204,8 +1246,9 @@ class EndToEndTestCase(unittest.TestCase):
|
|||
self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync")
|
||||
|
||||
# node affinity change should cause another rolling update and relocation of replica
|
||||
k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label)
|
||||
k8s.wait_for_pod_failover(master_nodes, 'spilo-role=replica,' + cluster_label)
|
||||
k8s.wait_for_pod_start('spilo-role=master,' + cluster_label)
|
||||
k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label)
|
||||
|
||||
except timeout_decorator.TimeoutError:
|
||||
print('Operator log: {}'.format(k8s.get_operator_log()))
|
||||
|
|
@ -1956,4 +1999,4 @@ class EndToEndTestCase(unittest.TestCase):
|
|||
return result_set
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
|
@ -231,7 +231,8 @@ func (c *Cluster) readPgUsersFromDatabase(userNames []string) (users spec.PgUser
|
|||
parameters[fields[0]] = fields[1]
|
||||
}
|
||||
|
||||
if strings.HasSuffix(rolname, c.OpConfig.RoleDeletionSuffix) {
|
||||
// consider NOLOGIN roles with deleted suffix as deprecated users
|
||||
if strings.HasSuffix(rolname, c.OpConfig.RoleDeletionSuffix) && !rolcanlogin {
|
||||
roldeleted = true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -104,18 +104,19 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
|
|||
if !(c.databaseAccessDisabled() || c.getNumberOfInstances(&newSpec.Spec) <= 0 || c.Spec.StandbyCluster != nil) {
|
||||
c.logger.Debug("syncing roles")
|
||||
if err = c.syncRoles(); err != nil {
|
||||
err = fmt.Errorf("could not sync roles: %v", err)
|
||||
return err
|
||||
// remember all cached users in c.pgUsers
|
||||
for cachedUserName, cachedUser := range c.pgUsersCache {
|
||||
c.pgUsers[cachedUserName] = cachedUser
|
||||
}
|
||||
c.logger.Errorf("could not sync roles: %v", err)
|
||||
}
|
||||
c.logger.Debug("syncing databases")
|
||||
if err = c.syncDatabases(); err != nil {
|
||||
err = fmt.Errorf("could not sync databases: %v", err)
|
||||
return err
|
||||
c.logger.Errorf("could not sync databases: %v", err)
|
||||
}
|
||||
c.logger.Debug("syncing prepared databases with schemas")
|
||||
if err = c.syncPreparedDatabases(); err != nil {
|
||||
err = fmt.Errorf("could not sync prepared database: %v", err)
|
||||
return err
|
||||
c.logger.Errorf("could not sync prepared database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -933,10 +934,7 @@ func (c *Cluster) syncRoles() (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// copy map for ProduceSyncRequests to include also system users
|
||||
for userName, pgUser := range c.pgUsers {
|
||||
newUsers[userName] = pgUser
|
||||
}
|
||||
// search also for system users
|
||||
for _, systemUser := range c.systemUsers {
|
||||
userNames = append(userNames, systemUser.Name)
|
||||
newUsers[systemUser.Name] = systemUser
|
||||
|
|
@ -950,13 +948,21 @@ func (c *Cluster) syncRoles() (err error) {
|
|||
// update pgUsers where a deleted role was found
|
||||
// so that they are skipped in ProduceSyncRequests
|
||||
for _, dbUser := range dbUsers {
|
||||
if originalUser, exists := deletedUsers[dbUser.Name]; exists {
|
||||
recreatedUser := c.pgUsers[originalUser]
|
||||
originalUsername, foundDeletedUser := deletedUsers[dbUser.Name]
|
||||
// check if original user does not exist in dbUsers
|
||||
_, originalUserAlreadyExists := dbUsers[originalUsername]
|
||||
if foundDeletedUser && !originalUserAlreadyExists {
|
||||
recreatedUser := c.pgUsers[originalUsername]
|
||||
recreatedUser.Deleted = true
|
||||
c.pgUsers[originalUser] = recreatedUser
|
||||
c.pgUsers[originalUsername] = recreatedUser
|
||||
}
|
||||
}
|
||||
|
||||
// last but not least copy pgUsers to newUsers to send to ProduceSyncRequests
|
||||
for _, pgUser := range c.pgUsers {
|
||||
newUsers[pgUser.Name] = pgUser
|
||||
}
|
||||
|
||||
pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(dbUsers, newUsers)
|
||||
if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil {
|
||||
return fmt.Errorf("error executing sync statements: %v", err)
|
||||
|
|
|
|||
|
|
@ -43,7 +43,8 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM
|
|||
|
||||
var reqs []spec.PgSyncUserRequest
|
||||
for name, newUser := range newUsers {
|
||||
// do not create user that exists in DB with deletion suffix
|
||||
// do not create user when there exists a user with the same name plus deletion suffix
|
||||
// instead request a renaming of the deleted user back to the original name (see * below)
|
||||
if newUser.Deleted {
|
||||
continue
|
||||
}
|
||||
|
|
@ -82,22 +83,28 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM
|
|||
}
|
||||
}
|
||||
|
||||
// No existing roles are deleted or stripped of role membership/flags
|
||||
// no existing roles are deleted or stripped of role membership/flags
|
||||
// but team roles will be renamed and denied from LOGIN
|
||||
for name, dbUser := range dbUsers {
|
||||
if _, exists := newUsers[name]; !exists {
|
||||
// toggle LOGIN flag based on role deletion
|
||||
userFlags := make([]string, len(dbUser.Flags))
|
||||
userFlags = append(userFlags, dbUser.Flags...)
|
||||
if dbUser.Deleted {
|
||||
dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagNoLogin, constants.RoleFlagLogin)
|
||||
// * user with deletion suffix and NOLOGIN found in database
|
||||
// grant back LOGIN and rename only if original user is wanted and does not exist in database
|
||||
originalName := strings.TrimSuffix(name, strategy.RoleDeletionSuffix)
|
||||
_, originalUserWanted := newUsers[originalName]
|
||||
_, originalUserAlreadyExists := dbUsers[originalName]
|
||||
if !originalUserWanted || originalUserAlreadyExists {
|
||||
continue
|
||||
}
|
||||
// a deleted dbUser has no NOLOGIN flag, so we can add the LOGIN flag
|
||||
dbUser.Flags = append(dbUser.Flags, constants.RoleFlagLogin)
|
||||
} else {
|
||||
// user found in database and not wanted in newUsers - replace LOGIN flag with NOLOGIN
|
||||
dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagLogin, constants.RoleFlagNoLogin)
|
||||
}
|
||||
if !util.IsEqualIgnoreOrder(userFlags, dbUser.Flags) {
|
||||
reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGsyncUserAlter, User: dbUser})
|
||||
}
|
||||
|
||||
// request ALTER ROLE to grant or revoke LOGIN
|
||||
reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGsyncUserAlter, User: dbUser})
|
||||
// request RENAME which will happen on behalf of the pgUser.Deleted field
|
||||
reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserRename, User: dbUser})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ var substractTest = []struct {
|
|||
}{
|
||||
{[]string{"a", "b", "c", "d"}, []string{"a", "b", "c", "d"}, []string{}, true},
|
||||
{[]string{"a", "b", "c", "d"}, []string{"a", "bb", "c", "d"}, []string{"b"}, false},
|
||||
{[]string{""}, []string{"b"}, []string{""}, false},
|
||||
{[]string{"a"}, []string{""}, []string{"a"}, false},
|
||||
}
|
||||
|
||||
var sliceContaintsTest = []struct {
|
||||
|
|
|
|||
Loading…
Reference in New Issue