This commit is contained in:
Felix Kunde 2025-10-24 07:39:06 +00:00 committed by GitHub
commit f28232e8f6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 59 additions and 16 deletions

View File

@ -1064,19 +1064,26 @@ func (c *Cluster) syncSecrets() error {
currentTime := time.Now() currentTime := time.Now()
for secretUsername, generatedSecret := range generatedSecrets { for secretUsername, generatedSecret := range generatedSecrets {
secret, err := c.KubeClient.Secrets(generatedSecret.Namespace).Create(context.TODO(), generatedSecret, metav1.CreateOptions{}) pgUserDegraded := false
createdSecret, err := c.KubeClient.Secrets(generatedSecret.Namespace).Create(context.TODO(), generatedSecret, metav1.CreateOptions{})
if err == nil { if err == nil {
c.Secrets[secret.UID] = secret c.Secrets[createdSecret.UID] = createdSecret
c.logger.Infof("created new secret %s, namespace: %s, uid: %s", util.NameFromMeta(secret.ObjectMeta), generatedSecret.Namespace, secret.UID) c.logger.Infof("created new secret %s, namespace: %s, uid: %s", util.NameFromMeta(createdSecret.ObjectMeta), generatedSecret.Namespace, createdSecret.UID)
continue continue
} }
if k8sutil.ResourceAlreadyExists(err) { if k8sutil.ResourceAlreadyExists(err) {
if err = c.updateSecret(secretUsername, generatedSecret, &retentionUsers, currentTime); err != nil { updatedSecret, err := c.updateSecret(secretUsername, generatedSecret, &retentionUsers, currentTime)
c.logger.Warningf("syncing secret %s failed: %v", util.NameFromMeta(secret.ObjectMeta), err) if err == nil {
c.Secrets[updatedSecret.UID] = updatedSecret
continue
} }
c.logger.Warningf("syncing secret %s failed: %v", util.NameFromMeta(updatedSecret.ObjectMeta), err)
pgUserDegraded = true
} else { } else {
return fmt.Errorf("could not create secret for user %s: in namespace %s: %v", secretUsername, generatedSecret.Namespace, err) c.logger.Warningf("could not create secret for user %s: in namespace %s: %v", secretUsername, generatedSecret.Namespace, err)
pgUserDegraded = true
} }
c.updatePgUser(secretUsername, pgUserDegraded)
} }
// remove rotation users that exceed the retention interval // remove rotation users that exceed the retention interval
@ -1105,7 +1112,7 @@ func (c *Cluster) updateSecret(
secretUsername string, secretUsername string,
generatedSecret *v1.Secret, generatedSecret *v1.Secret,
retentionUsers *[]string, retentionUsers *[]string,
currentTime time.Time) error { currentTime time.Time) (*v1.Secret, error) {
var ( var (
secret *v1.Secret secret *v1.Secret
err error err error
@ -1115,7 +1122,7 @@ func (c *Cluster) updateSecret(
// get the secret first // get the secret first
if secret, err = c.KubeClient.Secrets(generatedSecret.Namespace).Get(context.TODO(), generatedSecret.Name, metav1.GetOptions{}); err != nil { if secret, err = c.KubeClient.Secrets(generatedSecret.Namespace).Get(context.TODO(), generatedSecret.Name, metav1.GetOptions{}); err != nil {
return fmt.Errorf("could not get current secret: %v", err) return generatedSecret, fmt.Errorf("could not get current secret: %v", err)
} }
c.Secrets[secret.UID] = secret c.Secrets[secret.UID] = secret
@ -1211,24 +1218,22 @@ func (c *Cluster) updateSecret(
if updateSecret { if updateSecret {
c.logger.Infof("%s", updateSecretMsg) c.logger.Infof("%s", updateSecretMsg)
if secret, err = c.KubeClient.Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil { if secret, err = c.KubeClient.Secrets(secret.Namespace).Update(context.TODO(), secret, metav1.UpdateOptions{}); err != nil {
return fmt.Errorf("could not update secret %s: %v", secretName, err) return secret, fmt.Errorf("could not update secret %s: %v", secretName, err)
} }
c.Secrets[secret.UID] = secret
} }
if changed, _ := c.compareAnnotations(secret.Annotations, generatedSecret.Annotations, nil); changed { if changed, _ := c.compareAnnotations(secret.Annotations, generatedSecret.Annotations, nil); changed {
patchData, err := metaAnnotationsPatch(generatedSecret.Annotations) patchData, err := metaAnnotationsPatch(generatedSecret.Annotations)
if err != nil { if err != nil {
return fmt.Errorf("could not form patch for secret %q annotations: %v", secret.Name, err) return secret, fmt.Errorf("could not form patch for secret %q annotations: %v", secret.Name, err)
} }
secret, err = c.KubeClient.Secrets(secret.Namespace).Patch(context.TODO(), secret.Name, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) secret, err = c.KubeClient.Secrets(secret.Namespace).Patch(context.TODO(), secret.Name, types.MergePatchType, []byte(patchData), metav1.PatchOptions{})
if err != nil { if err != nil {
return fmt.Errorf("could not patch annotations for secret %q: %v", secret.Name, err) return secret, fmt.Errorf("could not patch annotations for secret %q: %v", secret.Name, err)
} }
c.Secrets[secret.UID] = secret
} }
return nil return secret, nil
} }
func (c *Cluster) rotatePasswordInSecret( func (c *Cluster) rotatePasswordInSecret(
@ -1334,6 +1339,23 @@ func (c *Cluster) rotatePasswordInSecret(
return updateSecretMsg, nil return updateSecretMsg, nil
} }
func (c *Cluster) updatePgUser(secretUsername string, degraded bool) {
for key, pgUser := range c.pgUsers {
if pgUser.Name == secretUsername {
pgUser.Degraded = degraded
c.pgUsers[key] = pgUser
return
}
}
for key, pgUser := range c.systemUsers {
if pgUser.Name == secretUsername {
pgUser.Degraded = degraded
c.systemUsers[key] = pgUser
return
}
}
}
func (c *Cluster) syncRoles() (err error) { func (c *Cluster) syncRoles() (err error) {
c.setProcessName("syncing roles") c.setProcessName("syncing roles")

View File

@ -12,9 +12,12 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
k8stesting "k8s.io/client-go/testing"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/zalando/postgres-operator/mocks" "github.com/zalando/postgres-operator/mocks"
@ -50,6 +53,16 @@ func newFakeK8sSyncClient() (k8sutil.KubernetesClient, *fake.Clientset) {
} }
func newFakeK8sSyncSecretsClient() (k8sutil.KubernetesClient, *fake.Clientset) { func newFakeK8sSyncSecretsClient() (k8sutil.KubernetesClient, *fake.Clientset) {
// add a reactor that checks namespace existence before creating secrets
clientSet.PrependReactor("create", "secrets", func(action k8stesting.Action) (bool, runtime.Object, error) {
createAction := action.(k8stesting.CreateAction)
secret := createAction.GetObject().(*v1.Secret)
if secret.Namespace != "default" {
return true, nil, errors.New("namespace does not exist")
}
return false, nil, nil
})
return k8sutil.KubernetesClient{ return k8sutil.KubernetesClient{
SecretsGetter: clientSet.CoreV1(), SecretsGetter: clientSet.CoreV1(),
}, clientSet }, clientSet
@ -810,7 +823,7 @@ func TestUpdateSecret(t *testing.T) {
}, },
Spec: acidv1.PostgresSpec{ Spec: acidv1.PostgresSpec{
Databases: map[string]string{dbname: dbowner}, Databases: map[string]string{dbname: dbowner},
Users: map[string]acidv1.UserFlags{appUser: {}, "bar": {}, dbowner: {}}, Users: map[string]acidv1.UserFlags{appUser: {}, "bar": {}, dbowner: {}, "not-exist.test_user": {}},
UsersIgnoringSecretRotation: []string{"bar"}, UsersIgnoringSecretRotation: []string{"bar"},
UsersWithInPlaceSecretRotation: []string{dbowner}, UsersWithInPlaceSecretRotation: []string{dbowner},
Streams: []acidv1.Stream{ Streams: []acidv1.Stream{
@ -842,6 +855,7 @@ func TestUpdateSecret(t *testing.T) {
PasswordRotationInterval: 1, PasswordRotationInterval: 1,
PasswordRotationUserRetention: 3, PasswordRotationUserRetention: 3,
}, },
EnableCrossNamespaceSecret: true,
Resources: config.Resources{ Resources: config.Resources{
ClusterLabels: map[string]string{"application": "spilo"}, ClusterLabels: map[string]string{"application": "spilo"},
ClusterNameLabel: "cluster-name", ClusterNameLabel: "cluster-name",
@ -864,8 +878,10 @@ func TestUpdateSecret(t *testing.T) {
allUsers := make(map[string]spec.PgUser) allUsers := make(map[string]spec.PgUser)
for _, pgUser := range cluster.pgUsers { for _, pgUser := range cluster.pgUsers {
if !pgUser.Degraded {
allUsers[pgUser.Name] = pgUser allUsers[pgUser.Name] = pgUser
} }
}
for _, systemUser := range cluster.systemUsers { for _, systemUser := range cluster.systemUsers {
allUsers[systemUser.Name] = systemUser allUsers[systemUser.Name] = systemUser
} }

View File

@ -58,6 +58,7 @@ type PgUser struct {
IsDbOwner bool `yaml:"is_db_owner"` IsDbOwner bool `yaml:"is_db_owner"`
Deleted bool `yaml:"deleted"` Deleted bool `yaml:"deleted"`
Rotated bool `yaml:"rotated"` Rotated bool `yaml:"rotated"`
Degraded bool `yaml:"degraded"`
} }
func (user *PgUser) Valid() bool { func (user *PgUser) Valid() bool {

View File

@ -48,6 +48,10 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM
if newUser.Deleted { if newUser.Deleted {
continue continue
} }
// when the secret of the user could not be created or updated skip any database actions
if newUser.Degraded {
continue
}
dbUser, exists := dbUsers[name] dbUser, exists := dbUsers[name]
if !exists { if !exists {
reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser}) reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser})