Skip to content

Commit 04ad66f

Browse files
authored
stop retention user cleanup early again when DB connection attempt fails (#2999)
* stop retention user cleanup early again when DB connection attempt fails * add unit test and new returned error from updateSecret
1 parent 42bbead commit 04ad66f

File tree

3 files changed

+83
-17
lines changed

3 files changed

+83
-17
lines changed

pkg/cluster/database.go

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -281,9 +281,23 @@ func findUsersFromRotation(rotatedUsers []string, db *sql.DB) (map[string]string
281281
return extraUsers, nil
282282
}
283283

284-
func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string, db *sql.DB) error {
284+
func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string) error {
285285
c.setProcessName("checking for rotated users to remove from the database due to configured retention")
286-
extraUsers, err := findUsersFromRotation(rotatedUsers, db)
286+
287+
err := c.initDbConn()
288+
if err != nil {
289+
return fmt.Errorf("could not init db connection: %v", err)
290+
}
291+
defer func() {
292+
if c.connectionIsClosed() {
293+
return
294+
}
295+
if err := c.closeDbConn(); err != nil {
296+
c.logger.Errorf("could not close database connection after removing users exceeding configured retention interval: %v", err)
297+
}
298+
}()
299+
300+
extraUsers, err := findUsersFromRotation(rotatedUsers, c.pgDb)
287301
if err != nil {
288302
return fmt.Errorf("error when querying for deprecated users from password rotation: %v", err)
289303
}
@@ -304,7 +318,7 @@ func (c *Cluster) cleanupRotatedUsers(rotatedUsers []string, db *sql.DB) error {
304318
}
305319
if retentionDate.After(userCreationDate) {
306320
c.logger.Infof("dropping user %q due to configured days in password_rotation_user_retention", rotatedUser)
307-
if err = users.DropPgUser(rotatedUser, db); err != nil {
321+
if err = users.DropPgUser(rotatedUser, c.pgDb); err != nil {
308322
c.logger.Errorf("could not drop role %q: %v", rotatedUser, err)
309323
continue
310324
}

pkg/cluster/sync.go

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,7 +1078,7 @@ func (c *Cluster) syncSecrets() error {
10781078
c.Secrets[updatedSecret.UID] = updatedSecret
10791079
continue
10801080
}
1081-
errors = append(errors, fmt.Sprintf("syncing secret %s failed: %v", util.NameFromMeta(updatedSecret.ObjectMeta), err))
1081+
errors = append(errors, fmt.Sprintf("syncing secret %s failed: %v", util.NameFromMeta(generatedSecret.ObjectMeta), err))
10821082
pgUserDegraded = true
10831083
} else {
10841084
errors = append(errors, fmt.Sprintf("could not create secret for user %s: in namespace %s: %v", secretUsername, generatedSecret.Namespace, err))
@@ -1089,16 +1089,9 @@ func (c *Cluster) syncSecrets() error {
10891089

10901090
// remove rotation users that exceed the retention interval
10911091
if len(retentionUsers) > 0 {
1092-
err := c.initDbConn()
1093-
if err != nil {
1094-
errors = append(errors, fmt.Sprintf("could not init db connection: %v", err))
1095-
}
1096-
if err = c.cleanupRotatedUsers(retentionUsers, c.pgDb); err != nil {
1092+
if err := c.cleanupRotatedUsers(retentionUsers); err != nil {
10971093
errors = append(errors, fmt.Sprintf("error removing users exceeding configured retention interval: %v", err))
10981094
}
1099-
if err := c.closeDbConn(); err != nil {
1100-
errors = append(errors, fmt.Sprintf("could not close database connection after removing users exceeding configured retention interval: %v", err))
1101-
}
11021095
}
11031096

11041097
if len(errors) > 0 {
@@ -1187,13 +1180,18 @@ func (c *Cluster) updateSecret(
11871180
}
11881181
} else {
11891182
// username might not match if password rotation has been disabled again
1190-
if secretUsername != string(secret.Data["username"]) {
1183+
usernameFromSecret := string(secret.Data["username"])
1184+
if secretUsername != usernameFromSecret {
1185+
// handle edge case when manifest user conflicts with a user from prepared databases
1186+
if strings.Replace(usernameFromSecret, "-", "_", -1) == strings.Replace(secretUsername, "-", "_", -1) {
1187+
return nil, fmt.Errorf("could not update secret because of user name mismatch: expected: %s, got: %s", secretUsername, usernameFromSecret)
1188+
}
11911189
*retentionUsers = append(*retentionUsers, secretUsername)
11921190
secret.Data["username"] = []byte(secretUsername)
11931191
secret.Data["password"] = []byte(util.RandomPassword(constants.PasswordLength))
11941192
secret.Data["nextRotation"] = []byte{}
11951193
updateSecret = true
1196-
updateSecretMsg = fmt.Sprintf("secret %s does not contain the role %s - updating username and resetting password", secretName, secretUsername)
1194+
updateSecretMsg = fmt.Sprintf("secret does not contain the role %s - updating username and resetting password", secretUsername)
11971195
}
11981196
}
11991197

@@ -1223,18 +1221,18 @@ func (c *Cluster) updateSecret(
12231221
if updateSecret {
12241222
c.logger.Infof("%s", updateSecretMsg)
12251223
if secret, err = c.KubeClient.Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil {
1226-
return secret, fmt.Errorf("could not update secret %s: %v", secretName, err)
1224+
return nil, fmt.Errorf("could not update secret: %v", err)
12271225
}
12281226
}
12291227

12301228
if changed, _ := c.compareAnnotations(secret.Annotations, generatedSecret.Annotations, nil); changed {
12311229
patchData, err := metaAnnotationsPatch(generatedSecret.Annotations)
12321230
if err != nil {
1233-
return secret, fmt.Errorf("could not form patch for secret %q annotations: %v", secret.Name, err)
1231+
return nil, fmt.Errorf("could not form patch for secret annotations: %v", err)
12341232
}
12351233
secret, err = c.KubeClient.Secrets(secret.Namespace).Patch(context.TODO(), secret.Name, types.MergePatchType, []byte(patchData), metav1.PatchOptions{})
12361234
if err != nil {
1237-
return secret, fmt.Errorf("could not patch annotations for secret %q: %v", secret.Name, err)
1235+
return nil, fmt.Errorf("could not patch annotations for secret: %v", err)
12381236
}
12391237
}
12401238

pkg/cluster/sync_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -964,3 +964,57 @@ func TestUpdateSecret(t *testing.T) {
964964
t.Errorf("%s: updated secret does not contain expected username: expected %s, got %s", testName, appUser, currentUsername)
965965
}
966966
}
967+
968+
func TestUpdateSecretNameConflict(t *testing.T) {
969+
client, _ := newFakeK8sSyncSecretsClient()
970+
971+
clusterName := "acid-test-cluster"
972+
namespace := "default"
973+
secretTemplate := config.StringTemplate("{username}.{cluster}.credentials")
974+
975+
// define manifest user that has the same name as a prepared database owner user except for dashes vs underscores
976+
// because of this the operator cannot create both secrets because underscores are not allowed in k8s secret names
977+
pg := acidv1.Postgresql{
978+
ObjectMeta: metav1.ObjectMeta{
979+
Name: clusterName,
980+
Namespace: namespace,
981+
},
982+
Spec: acidv1.PostgresSpec{
983+
PreparedDatabases: map[string]acidv1.PreparedDatabase{"prepared": {DefaultUsers: true}},
984+
Users: map[string]acidv1.UserFlags{"prepared-owner-user": {}},
985+
Volume: acidv1.Volume{
986+
Size: "1Gi",
987+
},
988+
},
989+
}
990+
991+
var cluster = New(
992+
Config{
993+
OpConfig: config.Config{
994+
Auth: config.Auth{
995+
SuperUsername: "postgres",
996+
ReplicationUsername: "standby",
997+
SecretNameTemplate: secretTemplate,
998+
},
999+
Resources: config.Resources{
1000+
ClusterLabels: map[string]string{"application": "spilo"},
1001+
ClusterNameLabel: "cluster-name",
1002+
},
1003+
},
1004+
}, client, pg, logger, eventRecorder)
1005+
1006+
cluster.Name = clusterName
1007+
cluster.Namespace = namespace
1008+
cluster.pgUsers = map[string]spec.PgUser{}
1009+
1010+
// init all users
1011+
cluster.initUsers()
1012+
// create secrets and fail because of user name mismatch
1013+
// prepared-owner-user from manifest vs prepared_owner_user from prepared database
1014+
err := cluster.syncSecrets()
1015+
assert.Error(t, err)
1016+
1017+
// the order of secrets to sync is not deterministic, check only first part of the error message
1018+
expectedError := fmt.Sprintf("syncing secret %s failed: could not update secret because of user name mismatch", "default/prepared-owner-user.acid-test-cluster.credentials")
1019+
assert.Contains(t, err.Error(), expectedError)
1020+
}

0 commit comments

Comments
 (0)