From a08d1679f2a4aa69bd20a8fd6307a08deb4fa4ee Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 27 Aug 2024 09:58:32 +0200 Subject: [PATCH 01/34] align sync and update logs (#2738) --- pkg/cluster/cluster.go | 10 ++++----- pkg/cluster/connection_pooler.go | 4 ++-- pkg/cluster/database.go | 2 +- pkg/cluster/majorversionupgrade.go | 2 +- pkg/cluster/pod.go | 10 ++++----- pkg/cluster/resources.go | 17 ++++++++------- pkg/cluster/streams.go | 7 +++--- pkg/cluster/sync.go | 17 ++++++++------- pkg/cluster/util.go | 8 +++---- pkg/cluster/volumes.go | 34 +++++++++++++++--------------- pkg/controller/postgresql.go | 2 +- 11 files changed, 58 insertions(+), 55 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index d9997463a..b510613bf 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1014,7 +1014,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { initUsers := !sameUsers || !sameRotatedUsers || needPoolerUser || needStreamUser if initUsers { - c.logger.Debugf("initialize users") + c.logger.Debug("initialize users") if err := c.initUsers(); err != nil { c.logger.Errorf("could not init users - skipping sync of secrets and databases: %v", err) userInitFailed = true @@ -1023,7 +1023,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { } } if initUsers || annotationsChanged { - c.logger.Debugf("syncing secrets") + c.logger.Debug("syncing secrets") //TODO: mind the secrets of the deleted/new users if err := c.syncSecrets(); err != nil { c.logger.Errorf("could not sync secrets: %v", err) @@ -1065,7 +1065,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { // create if it did not exist if !oldSpec.Spec.EnableLogicalBackup && newSpec.Spec.EnableLogicalBackup { - c.logger.Debugf("creating backup cron job") + c.logger.Debug("creating backup cron job") if err := c.createLogicalBackupJob(); err != nil { c.logger.Errorf("could not create a k8s cron job for logical backups: %v", err) updateFailed = true @@ -1075,7 +1075,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { // delete if no longer needed if oldSpec.Spec.EnableLogicalBackup && !newSpec.Spec.EnableLogicalBackup { - c.logger.Debugf("deleting backup cron job") + c.logger.Debug("deleting backup cron job") if err := c.deleteLogicalBackupJob(); err != nil { c.logger.Errorf("could not delete a k8s cron job for logical backups: %v", err) updateFailed = true @@ -1095,7 +1095,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { // Roles and Databases if !userInitFailed && !(c.databaseAccessDisabled() || c.getNumberOfInstances(&c.Spec) <= 0 || c.Spec.StandbyCluster != nil) { - c.logger.Debugf("syncing roles") + c.logger.Debug("syncing roles") if err := c.syncRoles(); err != nil { c.logger.Errorf("could not sync roles: %v", err) updateFailed = true diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 25d4514d1..6cd46f745 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -591,7 +591,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { // Lack of connection pooler objects is not a fatal error, just log it if // it was present before in the manifest if c.ConnectionPooler[role] == nil || role == "" { - c.logger.Debugf("no connection pooler to delete") + c.logger.Debug("no connection pooler to delete") return nil } @@ -622,7 +622,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { // Repeat the same for the service object service := c.ConnectionPooler[role].Service if service == nil { - c.logger.Debugf("no connection pooler service object to delete") + c.logger.Debug("no connection pooler service object to delete") } else { err = c.KubeClient. diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index 094af4aca..aac877bcf 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -111,7 +111,7 @@ func (c *Cluster) pgConnectionString(dbname string) string { func (c *Cluster) databaseAccessDisabled() bool { if !c.OpConfig.EnableDBAccess { - c.logger.Debugf("database access is disabled") + c.logger.Debug("database access is disabled") } return !c.OpConfig.EnableDBAccess diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 6bf4f167b..3d9482b25 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -116,7 +116,7 @@ func (c *Cluster) majorVersionUpgrade() error { c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeNormal, "Major Version Upgrade", "starting major version upgrade on pod %s of %d pods", masterPod.Name, numberOfPods) upgradeCommand := fmt.Sprintf("set -o pipefail && /usr/bin/python3 /scripts/inplace_upgrade.py %d 2>&1 | tee last_upgrade.log", numberOfPods) - c.logger.Debugf("checking if the spilo image runs with root or non-root (check for user id=0)") + c.logger.Debug("checking if the spilo image runs with root or non-root (check for user id=0)") resultIdCheck, errIdCheck := c.ExecCommand(podName, "/bin/bash", "-c", "/usr/bin/id -u") if errIdCheck != nil { c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "checking user id to run upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, errIdCheck) diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index a0db16164..890b60122 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -59,7 +59,7 @@ func (c *Cluster) markRollingUpdateFlagForPod(pod *v1.Pod, msg string) error { return nil } - c.logger.Debugf("mark rolling update annotation for %s: reason %s", pod.Name, msg) + c.logger.Infof("mark rolling update annotation for %s: reason %s", pod.Name, msg) flag := make(map[string]string) flag[rollingUpdatePodAnnotationKey] = strconv.FormatBool(true) @@ -110,7 +110,7 @@ func (c *Cluster) getRollingUpdateFlagFromPod(pod *v1.Pod) (flag bool) { } func (c *Cluster) deletePods() error { - c.logger.Debugln("deleting pods") + c.logger.Debug("deleting pods") pods, err := c.listPods() if err != nil { return err @@ -127,9 +127,9 @@ func (c *Cluster) deletePods() error { } } if len(pods) > 0 { - c.logger.Debugln("pods have been deleted") + c.logger.Debug("pods have been deleted") } else { - c.logger.Debugln("no pods to delete") + c.logger.Debug("no pods to delete") } return nil @@ -230,7 +230,7 @@ func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error { return fmt.Errorf("could not get node %q: %v", oldMaster.Spec.NodeName, err) } if !eol { - c.logger.Debugf("no action needed: master pod is already on a live node") + c.logger.Debug("no action needed: master pod is already on a live node") return nil } diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index f67498b61..6879ab928 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -187,7 +187,7 @@ func (c *Cluster) updateStatefulSet(newStatefulSet *appsv1.StatefulSet) error { c.logger.Warningf("could not scale down: %v", err) } } - c.logger.Debugf("updating statefulset") + c.logger.Debug("updating statefulset") patchData, err := specPatch(newStatefulSet.Spec) if err != nil { @@ -218,7 +218,7 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error { } statefulSetName := util.NameFromMeta(c.Statefulset.ObjectMeta) - c.logger.Debugf("replacing statefulset") + c.logger.Debug("replacing statefulset") // Delete the current statefulset without deleting the pods deletePropagationPolicy := metav1.DeletePropagationOrphan @@ -232,7 +232,7 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error { // make sure we clear the stored statefulset status if the subsequent create fails. c.Statefulset = nil // wait until the statefulset is truly deleted - c.logger.Debugf("waiting for the statefulset to be deleted") + c.logger.Debug("waiting for the statefulset to be deleted") err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout, func() (bool, error) { @@ -266,7 +266,7 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error { func (c *Cluster) deleteStatefulSet() error { c.setProcessName("deleting statefulset") - c.logger.Debugln("deleting statefulset") + c.logger.Debug("deleting statefulset") if c.Statefulset == nil { c.logger.Debug("there is no statefulset in the cluster") return nil @@ -349,7 +349,8 @@ func (c *Cluster) updateService(role PostgresRole, oldService *v1.Service, newSe } func (c *Cluster) deleteService(role PostgresRole) error { - c.logger.Debugf("deleting service %s", role) + c.setProcessName("deleting service") + c.logger.Debugf("deleting %s service", role) if c.Services[role] == nil { c.logger.Debugf("No service for %s role was found, nothing to delete", role) @@ -495,7 +496,7 @@ func (c *Cluster) deletePodDisruptionBudget() error { func (c *Cluster) deleteEndpoint(role PostgresRole) error { c.setProcessName("deleting endpoint") - c.logger.Debugln("deleting endpoint") + c.logger.Debugf("deleting %s endpoint", role) if c.Endpoints[role] == nil { c.logger.Debugf("there is no %s endpoint in the cluster", role) return nil @@ -543,7 +544,7 @@ func (c *Cluster) deletePatroniResources() error { func (c *Cluster) deletePatroniConfigMap(suffix string) error { c.setProcessName("deleting Patroni config map") - c.logger.Debugln("deleting Patroni config map") + c.logger.Debugf("deleting %s Patroni config map", suffix) cm := c.PatroniConfigMaps[suffix] if cm == nil { c.logger.Debugf("there is no %s Patroni config map in the cluster", suffix) @@ -565,7 +566,7 @@ func (c *Cluster) deletePatroniConfigMap(suffix string) error { func (c *Cluster) deletePatroniEndpoint(suffix string) error { c.setProcessName("deleting Patroni endpoint") - c.logger.Debugln("deleting Patroni endpoint") + c.logger.Debugf("deleting %s Patroni endpoint", suffix) ep := c.PatroniEndpoints[suffix] if ep == nil { c.logger.Debugf("there is no %s Patroni endpoint in the cluster", suffix) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 9a31edc28..f08376673 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -46,11 +46,13 @@ func (c *Cluster) updateStreams(newEventStreams *zalandov1.FabricEventStream) (p func (c *Cluster) deleteStream(appId string) error { c.setProcessName("deleting event stream") + c.logger.Debugf("deleting event stream with applicationId %s", appId) err := c.KubeClient.FabricEventStreams(c.Streams[appId].Namespace).Delete(context.TODO(), c.Streams[appId].Name, metav1.DeleteOptions{}) if err != nil { return fmt.Errorf("could not delete event stream %q with applicationId %s: %v", c.Streams[appId].Name, appId, err) } + c.logger.Infof("event stream %q with applicationId %s has been successfully deleted", c.Streams[appId].Name, appId) delete(c.Streams, appId) return nil @@ -308,7 +310,7 @@ func (c *Cluster) syncStreams() error { _, err := c.KubeClient.CustomResourceDefinitions().Get(context.TODO(), constants.EventStreamCRDName, metav1.GetOptions{}) if k8sutil.ResourceNotFound(err) { - c.logger.Debugf("event stream CRD not installed, skipping") + c.logger.Debug("event stream CRD not installed, skipping") return nil } @@ -473,7 +475,7 @@ func (c *Cluster) syncStream(appId string) error { c.Streams[appId] = stream } if match, reason := c.compareStreams(&stream, desiredStreams); !match { - c.logger.Debugf("updating event streams with applicationId %s: %s", appId, reason) + c.logger.Infof("updating event streams with applicationId %s: %s", appId, reason) desiredStreams.ObjectMeta = stream.ObjectMeta updatedStream, err := c.updateStreams(desiredStreams) if err != nil { @@ -550,7 +552,6 @@ func (c *Cluster) cleanupRemovedStreams(appIds []string) error { if err != nil { errors = append(errors, fmt.Sprintf("failed deleting event streams with applicationId %s: %v", appId, err)) } - c.logger.Infof("event streams with applicationId %s have been successfully deleted", appId) } } diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index ee1713c05..d1a339001 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -300,6 +300,7 @@ func (c *Cluster) syncPatroniService() error { err error ) serviceName := fmt.Sprintf("%s-%s", c.Name, Patroni) + c.logger.Debugf("syncing %s service", serviceName) c.setProcessName("syncing %s service", serviceName) if svc, err = c.KubeClient.Services(c.Namespace).Get(context.TODO(), serviceName, metav1.GetOptions{}); err == nil { @@ -311,7 +312,7 @@ func (c *Cluster) syncPatroniService() error { c.setProcessName("updating %v service", serviceName) svc, err = c.KubeClient.Services(c.Namespace).Update(context.TODO(), svc, metav1.UpdateOptions{}) if err != nil { - return fmt.Errorf("could not update %s endpoint: %v", serviceName, err) + return fmt.Errorf("could not update %s service: %v", serviceName, err) } c.Services[Patroni] = svc } @@ -537,7 +538,7 @@ func (c *Cluster) syncStatefulSet() error { if err != nil { return fmt.Errorf("could not generate statefulset: %v", err) } - c.logger.Debugf("syncing statefulsets") + c.logger.Debug("syncing statefulsets") // check if there are still pods with a rolling update flag for _, pod := range pods { if c.getRollingUpdateFlagFromPod(&pod) { @@ -552,7 +553,7 @@ func (c *Cluster) syncStatefulSet() error { } if len(podsToRecreate) > 0 { - c.logger.Debugf("%d / %d pod(s) still need to be rotated", len(podsToRecreate), len(pods)) + c.logger.Infof("%d / %d pod(s) still need to be rotated", len(podsToRecreate), len(pods)) } // statefulset is already there, make sure we use its definition in order to compare with the spec. @@ -658,7 +659,7 @@ func (c *Cluster) syncStatefulSet() error { // statefulset or those that got their configuration from the outdated statefulset) if len(podsToRecreate) > 0 { if isSafeToRecreatePods { - c.logger.Debugln("performing rolling update") + c.logger.Info("performing rolling update") c.eventRecorder.Event(c.GetReference(), v1.EventTypeNormal, "Update", "Performing rolling update") if err := c.recreatePods(podsToRecreate, switchoverCandidates); err != nil { return fmt.Errorf("could not recreate pods: %v", err) @@ -971,7 +972,7 @@ func (c *Cluster) syncStandbyClusterConfiguration() error { // carries the request to change configuration through for _, pod := range pods { podName := util.NameFromMeta(pod.ObjectMeta) - c.logger.Debugf("patching Postgres config via Patroni API on pod %s with following options: %s", + c.logger.Infof("patching Postgres config via Patroni API on pod %s with following options: %s", podName, standbyOptionsToSet) if err = c.patroni.SetStandbyClusterParameters(&pod, standbyOptionsToSet); err == nil { return nil @@ -983,7 +984,7 @@ func (c *Cluster) syncStandbyClusterConfiguration() error { } func (c *Cluster) syncSecrets() error { - c.logger.Info("syncing secrets") + c.logger.Debug("syncing secrets") c.setProcessName("syncing secrets") generatedSecrets := c.generateUserSecrets() retentionUsers := make([]string, 0) @@ -993,7 +994,7 @@ func (c *Cluster) syncSecrets() error { secret, err := c.KubeClient.Secrets(generatedSecret.Namespace).Create(context.TODO(), generatedSecret, metav1.CreateOptions{}) if err == nil { c.Secrets[secret.UID] = secret - c.logger.Debugf("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(secret.ObjectMeta), generatedSecret.Namespace, secret.UID) continue } if k8sutil.ResourceAlreadyExists(err) { @@ -1134,7 +1135,7 @@ func (c *Cluster) updateSecret( } if updateSecret { - c.logger.Debugln(updateSecretMsg) + c.logger.Infof(updateSecretMsg) 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) } diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index e36d0c175..c570fcc3a 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -193,7 +193,7 @@ func logNiceDiff(log *logrus.Entry, old, new interface{}) { nice := nicediff.Diff(string(o), string(n), true) for _, s := range strings.Split(nice, "\n") { // " is not needed in the value to understand - log.Debugf(strings.ReplaceAll(s, "\"", "")) + log.Debug(strings.ReplaceAll(s, "\"", "")) } } @@ -209,7 +209,7 @@ func (c *Cluster) logStatefulSetChanges(old, new *appsv1.StatefulSet, isUpdate b logNiceDiff(c.logger, old.Spec, new.Spec) if !reflect.DeepEqual(old.Annotations, new.Annotations) { - c.logger.Debugf("metadata.annotation are different") + c.logger.Debug("metadata.annotation are different") logNiceDiff(c.logger, old.Annotations, new.Annotations) } @@ -280,7 +280,7 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { } if !c.OpConfig.EnableTeamsAPI { - c.logger.Debugf("team API is disabled") + c.logger.Debug("team API is disabled") return members, nil } @@ -416,7 +416,7 @@ func (c *Cluster) _waitPodLabelsReady(anyReplica bool) error { podsNumber = len(pods.Items) c.logger.Debugf("Waiting for %d pods to become ready", podsNumber) } else { - c.logger.Debugf("Waiting for any replica pod to become ready") + c.logger.Debug("Waiting for any replica pod to become ready") } err := retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout, diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index 2646acbb7..3a9a37cc1 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -66,7 +66,7 @@ func (c *Cluster) syncVolumes() error { } func (c *Cluster) syncUnderlyingEBSVolume() error { - c.logger.Infof("starting to sync EBS volumes: type, iops, throughput, and size") + c.logger.Debug("starting to sync EBS volumes: type, iops, throughput, and size") var ( err error @@ -136,7 +136,7 @@ func (c *Cluster) syncUnderlyingEBSVolume() error { } func (c *Cluster) populateVolumeMetaData() error { - c.logger.Infof("starting reading ebs meta data") + c.logger.Debug("starting reading ebs meta data") pvs, err := c.listPersistentVolumes() if err != nil { @@ -165,7 +165,7 @@ func (c *Cluster) populateVolumeMetaData() error { } if len(currentVolumes) != len(c.EBSVolumes) && len(c.EBSVolumes) > 0 { - c.logger.Debugf("number of ebs volumes (%d) discovered differs from already known volumes (%d)", len(currentVolumes), len(c.EBSVolumes)) + c.logger.Infof("number of ebs volumes (%d) discovered differs from already known volumes (%d)", len(currentVolumes), len(c.EBSVolumes)) } // reset map, operator is not responsible for dangling ebs volumes @@ -205,18 +205,18 @@ func (c *Cluster) syncVolumeClaims() error { if currentSize < manifestSize { pvc.Spec.Resources.Requests[v1.ResourceStorage] = newSize needsUpdate = true - c.logger.Debugf("persistent volume claim for volume %q needs to be resized", pvc.Name) + c.logger.Infof("persistent volume claim for volume %q needs to be resized", pvc.Name) } else { c.logger.Warningf("cannot shrink persistent volume") } } if needsUpdate { - c.logger.Debugf("updating persistent volume claim definition for volume %q", pvc.Name) + c.logger.Infof("updating persistent volume claim definition for volume %q", pvc.Name) if _, err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Update(context.TODO(), &pvc, metav1.UpdateOptions{}); err != nil { return fmt.Errorf("could not update persistent volume claim: %q", err) } - c.logger.Debugf("successfully updated persistent volume claim %q", pvc.Name) + c.logger.Infof("successfully updated persistent volume claim %q", pvc.Name) } else { c.logger.Debugf("volume claim for volume %q do not require updates", pvc.Name) } @@ -234,7 +234,7 @@ func (c *Cluster) syncVolumeClaims() error { } } - c.logger.Infof("volume claims have been synced successfully") + c.logger.Debug("volume claims have been synced successfully") return nil } @@ -255,7 +255,7 @@ func (c *Cluster) syncEbsVolumes() error { return fmt.Errorf("could not sync volumes: %v", err) } - c.logger.Infof("volumes have been synced successfully") + c.logger.Debug("volumes have been synced successfully") return nil } @@ -274,7 +274,7 @@ func (c *Cluster) listPersistentVolumeClaims() ([]v1.PersistentVolumeClaim, erro } func (c *Cluster) deletePersistentVolumeClaims() error { - c.logger.Debugln("deleting PVCs") + c.logger.Debug("deleting PVCs") pvcs, err := c.listPersistentVolumeClaims() if err != nil { return err @@ -286,9 +286,9 @@ func (c *Cluster) deletePersistentVolumeClaims() error { } } if len(pvcs) > 0 { - c.logger.Debugln("PVCs have been deleted") + c.logger.Debug("PVCs have been deleted") } else { - c.logger.Debugln("no PVCs to delete") + c.logger.Debug("no PVCs to delete") } return nil @@ -382,22 +382,22 @@ func (c *Cluster) resizeVolumes() error { if err != nil { return err } - c.logger.Debugf("updating persistent volume %q to %d", pv.Name, newSize) + c.logger.Infof("updating persistent volume %q to %d", pv.Name, newSize) if err := resizer.ResizeVolume(awsVolumeID, newSize); err != nil { return fmt.Errorf("could not resize EBS volume %q: %v", awsVolumeID, err) } - c.logger.Debugf("resizing the filesystem on the volume %q", pv.Name) + c.logger.Infof("resizing the filesystem on the volume %q", pv.Name) podName := getPodNameFromPersistentVolume(pv) if err := c.resizePostgresFilesystem(podName, []filesystems.FilesystemResizer{&filesystems.Ext234Resize{}}); err != nil { return fmt.Errorf("could not resize the filesystem on pod %q: %v", podName, err) } - c.logger.Debugf("filesystem resize successful on volume %q", pv.Name) + c.logger.Infof("filesystem resize successful on volume %q", pv.Name) pv.Spec.Capacity[v1.ResourceStorage] = newQuantity - c.logger.Debugf("updating persistent volume definition for volume %q", pv.Name) + c.logger.Infof("updating persistent volume definition for volume %q", pv.Name) if _, err := c.KubeClient.PersistentVolumes().Update(context.TODO(), pv, metav1.UpdateOptions{}); err != nil { return fmt.Errorf("could not update persistent volume: %q", err) } - c.logger.Debugf("successfully updated persistent volume %q", pv.Name) + c.logger.Infof("successfully updated persistent volume %q", pv.Name) if !compatible { c.logger.Warningf("volume %q is incompatible with all available resizing providers, consider switching storage_resize_mode to pvc or off", pv.Name) @@ -458,7 +458,7 @@ func (c *Cluster) executeEBSMigration() error { } if !hasGp2 { - c.logger.Infof("no EBS gp2 volumes left to migrate") + c.logger.Debugf("no EBS gp2 volumes left to migrate") return nil } } diff --git a/pkg/controller/postgresql.go b/pkg/controller/postgresql.go index 4466080b7..42d96278c 100644 --- a/pkg/controller/postgresql.go +++ b/pkg/controller/postgresql.go @@ -143,7 +143,7 @@ func (c *Controller) acquireInitialListOfClusters() error { if list, err = c.listClusters(metav1.ListOptions{ResourceVersion: "0"}); err != nil { return err } - c.logger.Debugf("acquiring initial list of clusters") + c.logger.Debug("acquiring initial list of clusters") for _, pg := range list.Items { // XXX: check the cluster status field instead if pg.Error != "" { From 2ae51fb9ceefc11cc761fb6ccec41089b41c5a82 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 27 Aug 2024 17:56:07 +0200 Subject: [PATCH 02/34] reflect linter feedback, remove unused argumnents and redundant type from arrays (#2739) * reflect linter feedback, remove unused argumnents and redundant literal definitions * add logical backup to TestCreate unit test --- pkg/cluster/cluster_test.go | 8 +++-- pkg/cluster/connection_pooler_test.go | 2 +- pkg/cluster/k8sres.go | 14 ++++----- pkg/cluster/k8sres_test.go | 42 +++++++++++++-------------- pkg/cluster/streams.go | 4 +-- pkg/cluster/streams_test.go | 36 +++++++++++------------ pkg/cluster/sync_test.go | 2 +- pkg/controller/util.go | 3 +- pkg/controller/util_test.go | 28 +++++++++--------- pkg/util/k8sutil/k8sutil.go | 7 ++--- 10 files changed, 72 insertions(+), 74 deletions(-) diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index bf3cb58ae..897ed6c0d 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -71,11 +71,11 @@ var cl = New( Spec: acidv1.PostgresSpec{ EnableConnectionPooler: util.True(), Streams: []acidv1.Stream{ - acidv1.Stream{ + { ApplicationId: "test-app", Database: "test_db", Tables: map[string]acidv1.StreamTable{ - "test_table": acidv1.StreamTable{ + "test_table": { EventType: "test-app.test", }, }, @@ -95,6 +95,7 @@ func TestCreate(t *testing.T) { client := k8sutil.KubernetesClient{ DeploymentsGetter: clientSet.AppsV1(), + CronJobsGetter: clientSet.BatchV1(), EndpointsGetter: clientSet.CoreV1(), PersistentVolumeClaimsGetter: clientSet.CoreV1(), PodDisruptionBudgetsGetter: clientSet.PolicyV1(), @@ -111,6 +112,7 @@ func TestCreate(t *testing.T) { Namespace: clusterNamespace, }, Spec: acidv1.PostgresSpec{ + EnableLogicalBackup: true, Volume: acidv1.Volume{ Size: "1Gi", }, @@ -1504,7 +1506,7 @@ func newCronJob(image, schedule string, vars []v1.EnvVar, mounts []v1.VolumeMoun Template: v1.PodTemplateSpec{ Spec: v1.PodSpec{ Containers: []v1.Container{ - v1.Container{ + { Name: "logical-backup", Image: image, Env: vars, diff --git a/pkg/cluster/connection_pooler_test.go b/pkg/cluster/connection_pooler_test.go index e6472d017..78d1c2527 100644 --- a/pkg/cluster/connection_pooler_test.go +++ b/pkg/cluster/connection_pooler_test.go @@ -969,7 +969,7 @@ func TestPoolerTLS(t *testing.T) { TLS: &acidv1.TLSDescription{ SecretName: tlsSecretName, CAFile: "ca.crt"}, AdditionalVolumes: []acidv1.AdditionalVolume{ - acidv1.AdditionalVolume{ + { Name: tlsSecretName, MountPath: mountPath, VolumeSource: v1.VolumeSource{ diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 8934b6b49..91e19e4c9 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -739,7 +739,7 @@ func (c *Cluster) generateSidecarContainers(sidecars []acidv1.Sidecar, } // adds common fields to sidecars -func patchSidecarContainers(in []v1.Container, volumeMounts []v1.VolumeMount, superUserName string, credentialsSecretName string, logger *logrus.Entry) []v1.Container { +func patchSidecarContainers(in []v1.Container, volumeMounts []v1.VolumeMount, superUserName string, credentialsSecretName string) []v1.Container { result := []v1.Container{} for _, container := range in { @@ -1444,7 +1444,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef containerName, containerName) } - sidecarContainers = patchSidecarContainers(sidecarContainers, volumeMounts, c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername), c.logger) + sidecarContainers = patchSidecarContainers(sidecarContainers, volumeMounts, c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername)) tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration) effectivePodPriorityClassName := util.Coalesce(spec.PodPriorityClassName, c.OpConfig.PodPriorityClassName) @@ -1598,7 +1598,7 @@ func (c *Cluster) generatePodAnnotations(spec *acidv1.PostgresSpec) map[string]s for k, v := range c.OpConfig.CustomPodAnnotations { annotations[k] = v } - if spec != nil || spec.PodAnnotations != nil { + if spec.PodAnnotations != nil { for k, v := range spec.PodAnnotations { annotations[k] = v } @@ -1875,18 +1875,16 @@ func (c *Cluster) generatePersistentVolumeClaimTemplate(volumeSize, volumeStorag func (c *Cluster) generateUserSecrets() map[string]*v1.Secret { secrets := make(map[string]*v1.Secret, len(c.pgUsers)+len(c.systemUsers)) - namespace := c.Namespace for username, pgUser := range c.pgUsers { //Skip users with no password i.e. human users (they'll be authenticated using pam) - secret := c.generateSingleUserSecret(pgUser.Namespace, pgUser) + secret := c.generateSingleUserSecret(pgUser) if secret != nil { secrets[username] = secret } - namespace = pgUser.Namespace } /* special case for the system user */ for _, systemUser := range c.systemUsers { - secret := c.generateSingleUserSecret(namespace, systemUser) + secret := c.generateSingleUserSecret(systemUser) if secret != nil { secrets[systemUser.Name] = secret } @@ -1895,7 +1893,7 @@ func (c *Cluster) generateUserSecrets() map[string]*v1.Secret { return secrets } -func (c *Cluster) generateSingleUserSecret(namespace string, pgUser spec.PgUser) *v1.Secret { +func (c *Cluster) generateSingleUserSecret(pgUser spec.PgUser) *v1.Secret { //Skip users with no password i.e. human users (they'll be authenticated using pam) if pgUser.Password == "" { if pgUser.Origin != spec.RoleOriginTeamsAPI { diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index f18861687..07c05962d 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1451,9 +1451,9 @@ func TestNodeAffinity(t *testing.T) { nodeAff := &v1.NodeAffinity{ RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{ NodeSelectorTerms: []v1.NodeSelectorTerm{ - v1.NodeSelectorTerm{ + { MatchExpressions: []v1.NodeSelectorRequirement{ - v1.NodeSelectorRequirement{ + { Key: "test-label", Operator: v1.NodeSelectorOpIn, Values: []string{ @@ -1673,7 +1673,7 @@ func TestTLS(t *testing.T) { TLS: &acidv1.TLSDescription{ SecretName: tlsSecretName, CAFile: "ca.crt"}, AdditionalVolumes: []acidv1.AdditionalVolume{ - acidv1.AdditionalVolume{ + { Name: tlsSecretName, MountPath: mountPath, VolumeSource: v1.VolumeSource{ @@ -2162,17 +2162,17 @@ func TestSidecars(t *testing.T) { Size: "1G", }, Sidecars: []acidv1.Sidecar{ - acidv1.Sidecar{ + { Name: "cluster-specific-sidecar", }, - acidv1.Sidecar{ + { Name: "cluster-specific-sidecar-with-resources", Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")}, }, }, - acidv1.Sidecar{ + { Name: "replace-sidecar", DockerImage: "override-image", }, @@ -2200,11 +2200,11 @@ func TestSidecars(t *testing.T) { "deprecated-global-sidecar": "image:123", }, SidecarContainers: []v1.Container{ - v1.Container{ + { Name: "global-sidecar", }, // will be replaced by a cluster specific sidecar with the same name - v1.Container{ + { Name: "replace-sidecar", Image: "replaced-image", }, @@ -2259,7 +2259,7 @@ func TestSidecars(t *testing.T) { }, } mounts := []v1.VolumeMount{ - v1.VolumeMount{ + { Name: "pgdata", MountPath: "/home/postgres/pgdata", }, @@ -2516,17 +2516,17 @@ func TestGenerateService(t *testing.T) { Size: "1G", }, Sidecars: []acidv1.Sidecar{ - acidv1.Sidecar{ + { Name: "cluster-specific-sidecar", }, - acidv1.Sidecar{ + { Name: "cluster-specific-sidecar-with-resources", Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")}, ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")}, }, }, - acidv1.Sidecar{ + { Name: "replace-sidecar", DockerImage: "override-image", }, @@ -2555,11 +2555,11 @@ func TestGenerateService(t *testing.T) { "deprecated-global-sidecar": "image:123", }, SidecarContainers: []v1.Container{ - v1.Container{ + { Name: "global-sidecar", }, // will be replaced by a cluster specific sidecar with the same name - v1.Container{ + { Name: "replace-sidecar", Image: "replaced-image", }, @@ -2654,27 +2654,27 @@ func newLBFakeClient() (k8sutil.KubernetesClient, *fake.Clientset) { func getServices(serviceType v1.ServiceType, sourceRanges []string, extTrafficPolicy, clusterName string) []v1.ServiceSpec { return []v1.ServiceSpec{ - v1.ServiceSpec{ + { ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy), LoadBalancerSourceRanges: sourceRanges, Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}}, Type: serviceType, }, - v1.ServiceSpec{ + { ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy), LoadBalancerSourceRanges: sourceRanges, Ports: []v1.ServicePort{{Name: clusterName + "-pooler", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}}, Selector: map[string]string{"connection-pooler": clusterName + "-pooler"}, Type: serviceType, }, - v1.ServiceSpec{ + { ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy), LoadBalancerSourceRanges: sourceRanges, Ports: []v1.ServicePort{{Name: "postgresql", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}}, Selector: map[string]string{"spilo-role": "replica", "application": "spilo", "cluster-name": clusterName}, Type: serviceType, }, - v1.ServiceSpec{ + { ExternalTrafficPolicy: v1.ServiceExternalTrafficPolicyType(extTrafficPolicy), LoadBalancerSourceRanges: sourceRanges, Ports: []v1.ServicePort{{Name: clusterName + "-pooler-repl", Port: 5432, TargetPort: intstr.IntOrString{IntVal: 5432}}}, @@ -2894,7 +2894,7 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Sidecars: []acidv1.Sidecar{ - acidv1.Sidecar{ + { Name: sidecarName, }, }, @@ -3095,7 +3095,7 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Sidecars: []acidv1.Sidecar{ - acidv1.Sidecar{ + { Name: sidecarName, Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")}, @@ -3184,7 +3184,7 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Sidecars: []acidv1.Sidecar{ - acidv1.Sidecar{ + { Name: sidecarName, Resources: &acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")}, diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index f08376673..3d9cbae11 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -185,7 +185,7 @@ func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEvent } for tableName, table := range stream.Tables { streamSource := c.getEventStreamSource(stream, tableName, table.IdColumn) - streamFlow := getEventStreamFlow(stream, table.PayloadColumn) + streamFlow := getEventStreamFlow(table.PayloadColumn) streamSink := getEventStreamSink(stream, table.EventType) streamRecovery := getEventStreamRecovery(stream, table.RecoveryEventType, table.EventType) @@ -232,7 +232,7 @@ func (c *Cluster) getEventStreamSource(stream acidv1.Stream, tableName string, i } } -func getEventStreamFlow(stream acidv1.Stream, payloadColumn *string) zalandov1.EventStreamFlow { +func getEventStreamFlow(payloadColumn *string) zalandov1.EventStreamFlow { return zalandov1.EventStreamFlow{ Type: constants.EventStreamFlowPgGenericType, PayloadColumn: payloadColumn, diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 6091210b5..92d28663e 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -56,12 +56,12 @@ var ( ApplicationId: appId, Database: "foo", Tables: map[string]acidv1.StreamTable{ - "data.bar": acidv1.StreamTable{ + "data.bar": { EventType: "stream-type-a", IdColumn: k8sutil.StringToPointer("b_id"), PayloadColumn: k8sutil.StringToPointer("b_payload"), }, - "data.foobar": acidv1.StreamTable{ + "data.foobar": { EventType: "stream-type-b", RecoveryEventType: "stream-type-b-dlq", }, @@ -94,7 +94,7 @@ var ( "team": "acid", }, OwnerReferences: []metav1.OwnerReference{ - metav1.OwnerReference{ + { APIVersion: "apps/v1", Kind: "StatefulSet", Name: "acid-test-cluster", @@ -105,7 +105,7 @@ var ( Spec: zalandov1.FabricEventStreamSpec{ ApplicationId: appId, EventStreams: []zalandov1.EventStream{ - zalandov1.EventStream{ + { EventStreamFlow: zalandov1.EventStreamFlow{ PayloadColumn: k8sutil.StringToPointer("b_payload"), Type: constants.EventStreamFlowPgGenericType, @@ -144,7 +144,7 @@ var ( Type: constants.EventStreamSourcePGType, }, }, - zalandov1.EventStream{ + { EventStreamFlow: zalandov1.EventStreamFlow{ Type: constants.EventStreamFlowPgGenericType, }, @@ -241,7 +241,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test1": acidv1.StreamTable{ + "test1": { EventType: "stream-type-a", }, }, @@ -249,7 +249,7 @@ func TestHasSlotsInSync(t *testing.T) { }, }, actualSlots: map[string]map[string]string{ - slotName: map[string]string{ + slotName: { "databases": dbName, "plugin": constants.EventStreamSourcePluginType, "type": "logical", @@ -268,7 +268,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test1": acidv1.StreamTable{ + "test1": { EventType: "stream-type-a", }, }, @@ -289,7 +289,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test1": acidv1.StreamTable{ + "test1": { EventType: "stream-type-a", }, }, @@ -312,7 +312,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test1": acidv1.StreamTable{ + "test1": { EventType: "stream-type-a", }, }, @@ -326,7 +326,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test2": acidv1.StreamTable{ + "test2": { EventType: "stream-type-b", }, }, @@ -334,7 +334,7 @@ func TestHasSlotsInSync(t *testing.T) { }, }, actualSlots: map[string]map[string]string{ - slotName: map[string]string{ + slotName: { "databases": dbName, "plugin": constants.EventStreamSourcePluginType, "type": "logical", @@ -353,7 +353,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test1": acidv1.StreamTable{ + "test1": { EventType: "stream-type-a", }, }, @@ -367,7 +367,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test2": acidv1.StreamTable{ + "test2": { EventType: "stream-type-b", }, }, @@ -375,7 +375,7 @@ func TestHasSlotsInSync(t *testing.T) { }, }, actualSlots: map[string]map[string]string{ - slotName: map[string]string{ + slotName: { "databases": dbName, "plugin": constants.EventStreamSourcePluginType, "type": "logical", @@ -394,7 +394,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test1": acidv1.StreamTable{ + "test1": { EventType: "stream-type-a", }, }, @@ -408,7 +408,7 @@ func TestHasSlotsInSync(t *testing.T) { "type": "logical", }, Publication: map[string]acidv1.StreamTable{ - "test2": acidv1.StreamTable{ + "test2": { EventType: "stream-type-b", }, }, @@ -416,7 +416,7 @@ func TestHasSlotsInSync(t *testing.T) { }, }, actualSlots: map[string]map[string]string{ - slotName: map[string]string{ + slotName: { "databases": dbName, "plugin": constants.EventStreamSourcePluginType, "type": "logical", diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go index 46d1be5b7..d45a193cb 100644 --- a/pkg/cluster/sync_test.go +++ b/pkg/cluster/sync_test.go @@ -644,7 +644,7 @@ func TestUpdateSecret(t *testing.T) { ApplicationId: appId, Database: dbname, Tables: map[string]acidv1.StreamTable{ - "data.foo": acidv1.StreamTable{ + "data.foo": { EventType: "stream-type-b", }, }, diff --git a/pkg/controller/util.go b/pkg/controller/util.go index 5a3b23edc..59e608ad0 100644 --- a/pkg/controller/util.go +++ b/pkg/controller/util.go @@ -76,9 +76,8 @@ func (c *Controller) createOperatorCRD(desiredCrd *apiextv1.CustomResourceDefini context.TODO(), crd.Name, types.MergePatchType, patch, metav1.PatchOptions{}); err != nil { return fmt.Errorf("could not update customResourceDefinition %q: %v", crd.Name, err) } - } else { - c.logger.Infof("customResourceDefinition %q has been registered", crd.Name) } + c.logger.Infof("customResourceDefinition %q is registered", crd.Name) return wait.PollUntilContextTimeout(context.TODO(), c.config.CRDReadyWaitInterval, c.config.CRDReadyWaitTimeout, false, func(ctx context.Context) (bool, error) { c, err := c.KubeClient.CustomResourceDefinitions().Get(context.TODO(), desiredCrd.Name, metav1.GetOptions{}) diff --git a/pkg/controller/util_test.go b/pkg/controller/util_test.go index a4ca17728..4c3a9b356 100644 --- a/pkg/controller/util_test.go +++ b/pkg/controller/util_test.go @@ -132,7 +132,7 @@ func TestOldInfrastructureRoleFormat(t *testing.T) { for _, test := range testTable { roles, err := utilTestController.getInfrastructureRoles( []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: test.secretName, UserKey: "user", PasswordKey: "password", @@ -163,7 +163,7 @@ func TestNewInfrastructureRoleFormat(t *testing.T) { // one secret with one configmap { []spec.NamespacedName{ - spec.NamespacedName{ + { Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, }, @@ -187,11 +187,11 @@ func TestNewInfrastructureRoleFormat(t *testing.T) { // multiple standalone secrets { []spec.NamespacedName{ - spec.NamespacedName{ + { Namespace: v1.NamespaceDefault, Name: "infrastructureroles-new-test1", }, - spec.NamespacedName{ + { Namespace: v1.NamespaceDefault, Name: "infrastructureroles-new-test2", }, @@ -248,7 +248,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { // only new CRD format { []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, @@ -262,7 +262,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { spec.NamespacedName{}, "", []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, @@ -280,7 +280,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { spec.NamespacedName{}, "secretname: infrastructureroles-new-test, userkey: test-user, passwordkey: test-password, rolekey: test-role", []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, @@ -298,7 +298,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { spec.NamespacedName{}, "secretname: infrastructureroles-new-test, userkey: test-user, passwordkey: test-password, defaultrolevalue: test-role", []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, @@ -319,7 +319,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { }, "", []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesOldSecretName, @@ -334,7 +334,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { // both formats for CRD { []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, @@ -351,7 +351,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { }, "", []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, @@ -361,7 +361,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { RoleKey: "test-role", Template: false, }, - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesOldSecretName, @@ -382,7 +382,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { }, "secretname: infrastructureroles-new-test, userkey: test-user, passwordkey: test-password, rolekey: test-role", []*config.InfrastructureRole{ - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesNewSecretName, @@ -392,7 +392,7 @@ func TestInfrastructureRoleDefinitions(t *testing.T) { RoleKey: "test-role", Template: false, }, - &config.InfrastructureRole{ + { SecretName: spec.NamespacedName{ Namespace: v1.NamespaceDefault, Name: testInfrastructureRolesOldSecretName, diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index 7ae402fe3..de1fb605a 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -7,8 +7,6 @@ import ( b64 "encoding/base64" "encoding/json" - clientbatchv1 "k8s.io/client-go/kubernetes/typed/batch/v1" - apiacidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" zalandoclient "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned" acidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" @@ -24,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" + batchv1 "k8s.io/client-go/kubernetes/typed/batch/v1" corev1 "k8s.io/client-go/kubernetes/typed/core/v1" policyv1 "k8s.io/client-go/kubernetes/typed/policy/v1" rbacv1 "k8s.io/client-go/kubernetes/typed/rbac/v1" @@ -59,9 +58,9 @@ type KubernetesClient struct { appsv1.StatefulSetsGetter appsv1.DeploymentsGetter rbacv1.RoleBindingsGetter + batchv1.CronJobsGetter policyv1.PodDisruptionBudgetsGetter apiextv1client.CustomResourceDefinitionsGetter - clientbatchv1.CronJobsGetter acidv1.OperatorConfigurationsGetter acidv1.PostgresTeamsGetter acidv1.PostgresqlsGetter @@ -373,7 +372,7 @@ func (mock *mockDeployment) Get(ctx context.Context, name string, opts metav1.Ge Template: v1.PodTemplateSpec{ Spec: v1.PodSpec{ Containers: []v1.Container{ - v1.Container{ + { Image: "pooler:1.0", }, }, From a09b7655c9f20675c8d07b579ef0528f092c930c Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 27 Aug 2024 18:13:39 +0200 Subject: [PATCH 03/34] update K8s version and reflect necessary changes (#2740) --- Makefile | 2 +- go.mod | 25 ++++++------ go.sum | 56 +++++++++++++++------------ kubectl-pg/go.mod | 33 ++++++++-------- kubectl-pg/go.sum | 76 +++++++++++++++++++------------------ pkg/cluster/k8sres.go | 2 +- pkg/cluster/volumes_test.go | 4 +- 7 files changed, 106 insertions(+), 92 deletions(-) diff --git a/Makefile b/Makefile index 3b7ae4ede..5944b6b8f 100644 --- a/Makefile +++ b/Makefile @@ -78,7 +78,7 @@ mocks: GO111MODULE=on go generate ./... tools: - GO111MODULE=on go get -d k8s.io/client-go@kubernetes-1.28.12 + GO111MODULE=on go get -d k8s.io/client-go@kubernetes-1.30.4 GO111MODULE=on go install github.com/golang/mock/mockgen@v1.6.0 GO111MODULE=on go mod tidy diff --git a/go.mod b/go.mod index c1b36d6a5..69037040e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zalando/postgres-operator -go 1.22 +go 1.22.0 require ( github.com/aws/aws-sdk-go v1.53.8 @@ -14,18 +14,18 @@ require ( golang.org/x/crypto v0.26.0 golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.28.12 + k8s.io/api v0.30.4 k8s.io/apiextensions-apiserver v0.25.9 - k8s.io/apimachinery v0.28.12 - k8s.io/client-go v0.28.12 + k8s.io/apimachinery v0.30.4 + k8s.io/client-go v0.30.4 k8s.io/code-generator v0.25.9 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect - github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect @@ -36,6 +36,7 @@ require ( github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -46,11 +47,12 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/oauth2 v0.10.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.23.0 // indirect golang.org/x/term v0.23.0 // indirect @@ -62,10 +64,11 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index f882a95bd..d90bfdb5b 100644 --- a/go.sum +++ b/go.sum @@ -6,14 +6,13 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= @@ -34,6 +33,7 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -45,6 +45,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -80,10 +82,12 @@ github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+p github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= -github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= +github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= +github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -130,8 +134,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8= +golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -186,29 +190,31 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.28.12 h1:C2hpsaso18pqn0Dmkfnbv/YCctozTC3KGGuZ6bF7zhQ= -k8s.io/api v0.28.12/go.mod h1:qjswI+whxvf9LAKD4sEYHfy+WgHGWeH+H5sCRQMwZAQ= +k8s.io/api v0.30.4 h1:XASIELmW8w8q0i1Y4124LqPoWMycLjyQti/fdYHYjCs= +k8s.io/api v0.30.4/go.mod h1:ZqniWRKu7WIeLijbbzetF4U9qZ03cg5IRwl8YVs8mX0= k8s.io/apiextensions-apiserver v0.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8= k8s.io/apiextensions-apiserver v0.25.9/go.mod h1:ijGxmSG1GLOEaWhTuaEr0M7KUeia3mWCZa6FFQqpt1M= -k8s.io/apimachinery v0.28.12 h1:VepMEVOi9o7L/4wMAXJq+3BK9tqBIeerTB+HSOTKeo0= -k8s.io/apimachinery v0.28.12/go.mod h1:zUG757HaKs6Dc3iGtKjzIpBfqTM4yiRsEe3/E7NX15o= -k8s.io/client-go v0.28.12 h1:li7iRPRQF3vDki6gTxT/kXWJvw3BkJSdjVPVhDTZQec= -k8s.io/client-go v0.28.12/go.mod h1:yEzH2Z+nEGlrnKyHJWcJsbOr5tGdIj04dj1TVQOg0wE= +k8s.io/apimachinery v0.30.4 h1:5QHQI2tInzr8LsT4kU/2+fSeibH1eIHswNx480cqIoY= +k8s.io/apimachinery v0.30.4/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.4 h1:eculUe+HPQoPbixfwmaSZGsKcOf7D288tH6hDAdd+wY= +k8s.io/client-go v0.30.4/go.mod h1:IBS0R/Mt0LHkNHF4E6n+SUDPG7+m2po6RZU7YHeOpzc= k8s.io/code-generator v0.25.9 h1:lgyAV9AIRYNxZxgLRXqsCAtqJLHvakot41CjEqD5W0w= k8s.io/code-generator v0.25.9/go.mod h1:DHfpdhSUrwqF0f4oLqCtF8gYbqlndNetjBEz45nWzJI= k8s.io/gengo v0.0.0-20220902162205-c0856e24416d h1:U9tB195lKdzwqicbJvyJeOXV7Klv+wNAWENRnXEGi08= k8s.io/gengo v0.0.0-20220902162205-c0856e24416d/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= +k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= +k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 6a658eb49..67c83354b 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -1,20 +1,20 @@ module github.com/zalando/postgres-operator/kubectl-pg -go 1.22 +go 1.22.0 require ( github.com/spf13/cobra v1.8.1 github.com/spf13/viper v1.19.0 - github.com/zalando/postgres-operator v1.12.2 - k8s.io/api v0.28.12 + github.com/zalando/postgres-operator v1.13.0 + k8s.io/api v0.30.4 k8s.io/apiextensions-apiserver v0.25.9 - k8s.io/apimachinery v0.28.12 - k8s.io/client-go v0.28.12 + k8s.io/apimachinery v0.30.4 + k8s.io/client-go v0.30.4 ) require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -23,9 +23,9 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.4.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -40,6 +40,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -50,13 +51,13 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.23.0 // indirect + golang.org/x/crypto v0.26.0 // indirect golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect - golang.org/x/net v0.23.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.15.0 // indirect + golang.org/x/sys v0.23.0 // indirect + golang.org/x/term v0.23.0 // indirect + golang.org/x/text v0.17.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 // indirect @@ -64,10 +65,10 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect - k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index 5d8a2a57f..c873d0e37 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -6,13 +6,12 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -32,8 +31,9 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6 github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -42,6 +42,8 @@ github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLe github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= @@ -78,10 +80,12 @@ github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+p github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= -github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= -github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= +github.com/onsi/gomega v1.31.0 h1:54UJxxj6cPInHS3a35wm6BK/F9nHYueZ1NVujHDrnXE= +github.com/onsi/gomega v1.31.0/go.mod h1:DW9aCi7U6Yi40wNVAvT6kzFnEVEI5n3DloYBiKiT6zk= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -125,16 +129,16 @@ github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSW github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zalando/postgres-operator v1.12.2 h1:HJLrGSJLKYkvdpHIxlAKhXWTeRsgDQki2s9QOyApUX0= -github.com/zalando/postgres-operator v1.12.2/go.mod h1:tKNY4pMjnr5BhuzGiGngf1SPJ7K1vVRCmMkfmV9KZoQ= +github.com/zalando/postgres-operator v1.13.0 h1:T9Mb+ZRQyTxXbagIK66GLVGCwM3661aX2lOkNpax4s8= +github.com/zalando/postgres-operator v1.13.0/go.mod h1:WiMEKzUny2lJHYle+7+D/5BhlvPn8prl76rEDYLsQAg= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -146,8 +150,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -162,18 +166,18 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -181,8 +185,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -206,23 +210,23 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -k8s.io/api v0.28.12 h1:C2hpsaso18pqn0Dmkfnbv/YCctozTC3KGGuZ6bF7zhQ= -k8s.io/api v0.28.12/go.mod h1:qjswI+whxvf9LAKD4sEYHfy+WgHGWeH+H5sCRQMwZAQ= +k8s.io/api v0.30.4 h1:XASIELmW8w8q0i1Y4124LqPoWMycLjyQti/fdYHYjCs= +k8s.io/api v0.30.4/go.mod h1:ZqniWRKu7WIeLijbbzetF4U9qZ03cg5IRwl8YVs8mX0= k8s.io/apiextensions-apiserver v0.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8= k8s.io/apiextensions-apiserver v0.25.9/go.mod h1:ijGxmSG1GLOEaWhTuaEr0M7KUeia3mWCZa6FFQqpt1M= -k8s.io/apimachinery v0.28.12 h1:VepMEVOi9o7L/4wMAXJq+3BK9tqBIeerTB+HSOTKeo0= -k8s.io/apimachinery v0.28.12/go.mod h1:zUG757HaKs6Dc3iGtKjzIpBfqTM4yiRsEe3/E7NX15o= -k8s.io/client-go v0.28.12 h1:li7iRPRQF3vDki6gTxT/kXWJvw3BkJSdjVPVhDTZQec= -k8s.io/client-go v0.28.12/go.mod h1:yEzH2Z+nEGlrnKyHJWcJsbOr5tGdIj04dj1TVQOg0wE= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= -k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk= -k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/apimachinery v0.30.4 h1:5QHQI2tInzr8LsT4kU/2+fSeibH1eIHswNx480cqIoY= +k8s.io/apimachinery v0.30.4/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.4 h1:eculUe+HPQoPbixfwmaSZGsKcOf7D288tH6hDAdd+wY= +k8s.io/client-go v0.30.4/go.mod h1:IBS0R/Mt0LHkNHF4E6n+SUDPG7+m2po6RZU7YHeOpzc= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag= +k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 91e19e4c9..4e67dbd94 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1859,7 +1859,7 @@ func (c *Cluster) generatePersistentVolumeClaimTemplate(volumeSize, volumeStorag }, Spec: v1.PersistentVolumeClaimSpec{ AccessModes: []v1.PersistentVolumeAccessMode{v1.ReadWriteOnce}, - Resources: v1.ResourceRequirements{ + Resources: v1.VolumeResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceStorage: quantity, }, diff --git a/pkg/cluster/volumes_test.go b/pkg/cluster/volumes_test.go index 329224893..76b02e02e 100644 --- a/pkg/cluster/volumes_test.go +++ b/pkg/cluster/volumes_test.go @@ -165,7 +165,7 @@ func CreatePVCs(namespace string, clusterName string, labels labels.Set, n int, Labels: labels, }, Spec: v1.PersistentVolumeClaimSpec{ - Resources: v1.ResourceRequirements{ + Resources: v1.VolumeResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceStorage: storage1Gi, }, @@ -256,7 +256,7 @@ func initTestVolumesAndPods(client k8sutil.KubernetesClient, namespace, clustern Labels: labels, }, Spec: v1.PersistentVolumeClaimSpec{ - Resources: v1.ResourceRequirements{ + Resources: v1.VolumeResourceRequirements{ Requests: v1.ResourceList{ v1.ResourceStorage: storage1Gi, }, From 2e398120d2d0b3bb2b8bb239c6d49011ebe37e88 Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Wed, 28 Aug 2024 15:26:12 +0200 Subject: [PATCH 04/34] Implement major upgrade result annotations (#2727) Co-authored-by: Felix Kunde Co-authored-by: Polina Bungina <27892524+hughcapet@users.noreply.github.com> --- docs/administrator.md | 6 +++ e2e/tests/test_e2e.py | 81 ++++++++++++++++++++++++------ pkg/cluster/majorversionupgrade.go | 65 +++++++++++++++++++++++- 3 files changed, 135 insertions(+), 17 deletions(-) diff --git a/docs/administrator.md b/docs/administrator.md index 3552f958b..86ceca291 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -85,6 +85,12 @@ It is also possible to define `maintenanceWindows` in the Postgres manifest to better control when such automated upgrades should take place after increasing the version. +### Upgrade annotations + +When an upgrade is executed, the operator sets an annotation in the PostgreSQL resource, either `last-major-upgrade-success` if the upgrade succeeds, or `last-major-upgrade-failure` if it fails. The value of the annotation is a timestamp indicating when the upgrade occurred. + +If a PostgreSQL resource contains a failure annotation, the operator will not attempt to retry the upgrade during a sync event. To remove the failure annotation, you can revert the PostgreSQL version back to the current version. This action will trigger the removal of the failure annotation. + ## Non-default cluster domain If your cluster uses a DNS domain other than the default `cluster.local`, this diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 06e5c5231..f89e2fb86 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -1185,13 +1185,19 @@ class EndToEndTestCase(unittest.TestCase): @timeout_decorator.timeout(TEST_TIMEOUT_SEC) def test_major_version_upgrade(self): """ - Test major version upgrade + Test major version upgrade: with full upgrade, maintenance window, and annotation """ def check_version(): p = k8s.patroni_rest("acid-upgrade-test-0", "") version = p.get("server_version", 0) // 10000 return version + def get_annotations(): + pg_manifest = k8s.api.custom_objects_api.get_namespaced_custom_object( + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test") + annotations = pg_manifest["metadata"]["annotations"] + return annotations + k8s = self.k8s cluster_label = 'application=spilo,cluster-name=acid-upgrade-test' @@ -1209,30 +1215,33 @@ class EndToEndTestCase(unittest.TestCase): master_nodes, _ = k8s.get_cluster_nodes(cluster_labels=cluster_label) # should upgrade immediately - pg_patch_version_14 = { + pg_patch_version_13 = { "spec": { "postgresql": { - "version": "14" + "version": "13" } } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_14) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_13) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") - # should have finish failover 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) - self.eventuallyEqual(check_version, 14, "Version should be upgraded from 12 to 14") + self.eventuallyEqual(check_version, 13, "Version should be upgraded from 12 to 13") + + # check if annotation for last upgrade's success is set + annotations = get_annotations() + self.assertIsNotNone(annotations.get("last-major-upgrade-success"), "Annotation for last upgrade's success is not set") # should not upgrade because current time is not in maintenanceWindow current_time = datetime.now() maintenance_window_future = f"{(current_time+timedelta(minutes=60)).strftime('%H:%M')}-{(current_time+timedelta(minutes=120)).strftime('%H:%M')}" - pg_patch_version_15 = { + pg_patch_version_14 = { "spec": { "postgresql": { - "version": "15" + "version": "14" }, "maintenanceWindows": [ maintenance_window_future @@ -1240,21 +1249,23 @@ class EndToEndTestCase(unittest.TestCase): } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_14) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") - # should have finish failover k8s.wait_for_pod_failover(master_nodes, 'spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 14, "Version should not be upgraded") + self.eventuallyEqual(check_version, 13, "Version should not be upgraded") + + second_annotations = get_annotations() + self.assertIsNone(second_annotations.get("last-major-upgrade-failure"), "Annotation for last upgrade's failure should not be set") # change the version again to trigger operator sync maintenance_window_current = f"{(current_time-timedelta(minutes=30)).strftime('%H:%M')}-{(current_time+timedelta(minutes=30)).strftime('%H:%M')}" - pg_patch_version_16 = { + pg_patch_version_15 = { "spec": { "postgresql": { - "version": "16" + "version": "15" }, "maintenanceWindows": [ maintenance_window_current @@ -1263,14 +1274,52 @@ class EndToEndTestCase(unittest.TestCase): } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_16) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") - # should have finish failover 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) - self.eventuallyEqual(check_version, 16, "Version should be upgraded from 14 to 16") + self.eventuallyEqual(check_version, 15, "Version should be upgraded from 13 to 15") + + # check if annotation for last upgrade's success is updated after second upgrade + third_annotations = get_annotations() + self.assertIsNotNone(third_annotations.get("last-major-upgrade-success"), "Annotation for last upgrade's success is not set") + self.assertNotEqual(annotations.get("last-major-upgrade-success"), third_annotations.get("last-major-upgrade-success"), "Annotation for last upgrade's success is not updated") + + # test upgrade with failed upgrade annotation + pg_patch_version_16 = { + "metadata": { + "annotations": { + "last-major-upgrade-failure": "2024-01-02T15:04:05Z" + }, + }, + "spec": { + "postgresql": { + "version": "16" + }, + }, + } + k8s.api.custom_objects_api.patch_namespaced_custom_object( + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_16) + self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") + + k8s.wait_for_pod_failover(master_nodes, 'spilo-role=master,' + cluster_label) + k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) + k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) + self.eventuallyEqual(check_version, 15, "Version should not be upgraded because annotation for last upgrade's failure is set") + + # change the version back to 15 and should remove failure annotation + k8s.api.custom_objects_api.patch_namespaced_custom_object( + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15) + self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") + + 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) + + fourth_annotations = get_annotations() + self.assertIsNone(fourth_annotations.get("last-major-upgrade-failure"), "Annotation for last upgrade's failure is not removed") @timeout_decorator.timeout(TEST_TIMEOUT_SEC) def test_persistent_volume_claim_retention_policy(self): diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 3d9482b25..f51e42415 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -1,12 +1,16 @@ package cluster import ( + "context" + "encoding/json" "fmt" "strings" "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" ) // VersionMap Map of version numbers @@ -18,6 +22,11 @@ var VersionMap = map[string]int{ "16": 160000, } +const ( + majorVersionUpgradeSuccessAnnotation = "last-major-upgrade-success" + majorVersionUpgradeFailureAnnotation = "last-major-upgrade-failure" +) + // IsBiggerPostgresVersion Compare two Postgres version numbers func IsBiggerPostgresVersion(old string, new string) bool { oldN := VersionMap[old] @@ -54,6 +63,47 @@ func (c *Cluster) isUpgradeAllowedForTeam(owningTeam string) bool { return util.SliceContains(allowedTeams, owningTeam) } +func (c *Cluster) annotatePostgresResource(isSuccess bool) error { + annotations := make(map[string]string) + currentTime := metav1.Now().Format("2006-01-02T15:04:05Z") + if isSuccess { + annotations[majorVersionUpgradeSuccessAnnotation] = currentTime + } else { + annotations[majorVersionUpgradeFailureAnnotation] = currentTime + } + patchData, err := metaAnnotationsPatch(annotations) + if err != nil { + c.logger.Errorf("could not form patch for %s postgresql resource: %v", c.Name, err) + return err + } + _, err = c.KubeClient.Postgresqls(c.Namespace).Patch(context.Background(), c.Name, types.MergePatchType, patchData, metav1.PatchOptions{}) + if err != nil { + c.logger.Errorf("failed to patch annotations to postgresql resource: %v", err) + return err + } + return nil +} + +func (c *Cluster) removeFailuresAnnotation() error { + annotationToRemove := []map[string]string{ + { + "op": "remove", + "path": fmt.Sprintf("/metadata/annotations/%s", majorVersionUpgradeFailureAnnotation), + }, + } + removePatch, err := json.Marshal(annotationToRemove) + if err != nil { + c.logger.Errorf("could not form removal patch for %s postgresql resource: %v", c.Name, err) + return err + } + _, err = c.KubeClient.Postgresqls(c.Namespace).Patch(context.Background(), c.Name, types.JSONPatchType, removePatch, metav1.PatchOptions{}) + if err != nil { + c.logger.Errorf("failed to remove annotations from postgresql resource: %v", err) + return err + } + return nil +} + /* Execute upgrade when mode is set to manual or full or when the owning team is allowed for upgrade (and mode is "off"). @@ -69,10 +119,19 @@ func (c *Cluster) majorVersionUpgrade() error { desiredVersion := c.GetDesiredMajorVersionAsInt() if c.currentMajorVersion >= desiredVersion { + if _, exists := c.ObjectMeta.Annotations[majorVersionUpgradeFailureAnnotation]; exists { // if failure annotation exists, remove it + c.removeFailuresAnnotation() + c.logger.Infof("removing failure annotation as the cluster is already up to date") + } c.logger.Infof("cluster version up to date. current: %d, min desired: %d", c.currentMajorVersion, desiredVersion) return nil } + if _, exists := c.ObjectMeta.Annotations[majorVersionUpgradeFailureAnnotation]; exists { + c.logger.Infof("last major upgrade failed, skipping upgrade") + return nil + } + if !isInMainternanceWindow(c.Spec.MaintenanceWindows) { c.logger.Infof("skipping major version upgrade, not in maintenance window") return nil @@ -107,6 +166,7 @@ func (c *Cluster) majorVersionUpgrade() error { return nil } + isUpgradeSuccess := true numberOfPods := len(pods) if allRunning && masterPod != nil { c.logger.Infof("healthy cluster ready to upgrade, current: %d desired: %d", c.currentMajorVersion, desiredVersion) @@ -132,11 +192,14 @@ func (c *Cluster) majorVersionUpgrade() error { result, err = c.ExecCommand(podName, "/bin/su", "postgres", "-c", upgradeCommand) } if err != nil { + isUpgradeSuccess = false + c.annotatePostgresResource(isUpgradeSuccess) c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, err) return err } - c.logger.Infof("upgrade action triggered and command completed: %s", result[:100]) + c.annotatePostgresResource(isUpgradeSuccess) + c.logger.Infof("upgrade action triggered and command completed: %s", result[:100]) c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeNormal, "Major Version Upgrade", "upgrade from %d to %d finished", c.currentMajorVersion, desiredVersion) } } From c25dc57b9614df4a89e08c9c766b6f6ee64fb41f Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Tue, 10 Sep 2024 10:32:56 +0200 Subject: [PATCH 05/34] only skip upgrade if failed before after recheck version (#2755) --- pkg/cluster/majorversionupgrade.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index f51e42415..1c5a670eb 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -127,11 +127,6 @@ func (c *Cluster) majorVersionUpgrade() error { return nil } - if _, exists := c.ObjectMeta.Annotations[majorVersionUpgradeFailureAnnotation]; exists { - c.logger.Infof("last major upgrade failed, skipping upgrade") - return nil - } - if !isInMainternanceWindow(c.Spec.MaintenanceWindows) { c.logger.Infof("skipping major version upgrade, not in maintenance window") return nil @@ -162,10 +157,19 @@ func (c *Cluster) majorVersionUpgrade() error { // Recheck version with newest data from Patroni if c.currentMajorVersion >= desiredVersion { + if _, exists := c.ObjectMeta.Annotations[majorVersionUpgradeFailureAnnotation]; exists { // if failure annotation exists, remove it + c.removeFailuresAnnotation() + c.logger.Infof("removing failure annotation as the cluster is already up to date") + } c.logger.Infof("recheck cluster version is already up to date. current: %d, min desired: %d", c.currentMajorVersion, desiredVersion) return nil } + if _, exists := c.ObjectMeta.Annotations[majorVersionUpgradeFailureAnnotation]; exists { + c.logger.Infof("last major upgrade failed, skipping upgrade") + return nil + } + isUpgradeSuccess := true numberOfPods := len(pods) if allRunning && masterPod != nil { From 3ca86678ccfc7dfedfb49794ca072dff9a1b8983 Mon Sep 17 00:00:00 2001 From: Polina Bungina <27892524+hughcapet@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:11:46 +0200 Subject: [PATCH 06/34] Add major upgrade prechecks (#2772) Don't fail major upgrade (don't set annotation) if replica(s) are not (yet) streaming or replication lag is too high --- go.mod | 1 + go.sum | 2 ++ pkg/cluster/majorversionupgrade.go | 41 +++++++++++++++++++++++++++--- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 69037040e..d6390f45f 100644 --- a/go.mod +++ b/go.mod @@ -22,6 +22,7 @@ require ( ) require ( + github.com/Masterminds/semver v1.5.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect diff --git a/go.sum b/go.sum index d90bfdb5b..c7992fea0 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= +github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.53.8 h1:eoqGb1WOHIrCFKo1d51cMcnt1ralfLFaEqRkC5Zzv8k= diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 1c5a670eb..e8876dc49 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -6,6 +6,7 @@ import ( "fmt" "strings" + "github.com/Masterminds/semver" "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" v1 "k8s.io/api/core/v1" @@ -170,6 +171,38 @@ func (c *Cluster) majorVersionUpgrade() error { return nil } + members, err := c.patroni.GetClusterMembers(masterPod) + if err != nil { + c.logger.Error("could not get cluster members data from Patroni API, skipping major version upgrade") + return err + } + patroniData, err := c.patroni.GetMemberData(masterPod) + if err != nil { + c.logger.Error("could not get members data from Patroni API, skipping major version upgrade") + return err + } + patroniVer, err := semver.NewVersion(patroniData.Patroni.Version) + if err != nil { + c.logger.Error("error parsing Patroni version") + patroniVer, _ = semver.NewVersion("3.0.4") + } + verConstraint, _ := semver.NewConstraint(">= 3.0.4") + checkStreaming, _ := verConstraint.Validate(patroniVer) + + for _, member := range members { + if PostgresRole(member.Role) == Leader { + continue + } + if checkStreaming && member.State != "streaming" { + c.logger.Infof("skipping major version upgrade, replica %s is not streaming from primary", member.Name) + return nil + } + if member.Lag > 16*1024*1024 { + c.logger.Infof("skipping major version upgrade, replication lag on member %s is too high", member.Name) + return nil + } + } + isUpgradeSuccess := true numberOfPods := len(pods) if allRunning && masterPod != nil { @@ -187,19 +220,21 @@ func (c *Cluster) majorVersionUpgrade() error { } resultIdCheck = strings.TrimSuffix(resultIdCheck, "\n") - var result string + var result, scriptErrMsg string if resultIdCheck != "0" { c.logger.Infof("user id was identified as: %s, hence default user is non-root already", resultIdCheck) result, err = c.ExecCommand(podName, "/bin/bash", "-c", upgradeCommand) + scriptErrMsg, _ = c.ExecCommand(podName, "/bin/bash", "-c", "tail -n 1 last_upgrade.log") } else { c.logger.Infof("user id was identified as: %s, using su to reach the postgres user", resultIdCheck) result, err = c.ExecCommand(podName, "/bin/su", "postgres", "-c", upgradeCommand) + scriptErrMsg, _ = c.ExecCommand(podName, "/bin/bash", "-c", "tail -n 1 last_upgrade.log") } if err != nil { isUpgradeSuccess = false c.annotatePostgresResource(isUpgradeSuccess) - c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, err) - return err + c.eventRecorder.Eventf(c.GetReference(), v1.EventTypeWarning, "Major Version Upgrade", "upgrade from %d to %d FAILED: %v", c.currentMajorVersion, desiredVersion, scriptErrMsg) + return fmt.Errorf(scriptErrMsg) } c.annotatePostgresResource(isUpgradeSuccess) From 41f5fe1dc93fb33b2bca98590a3227bde4fd6949 Mon Sep 17 00:00:00 2001 From: Polina Bungina <27892524+hughcapet@users.noreply.github.com> Date: Tue, 15 Oct 2024 14:05:39 +0200 Subject: [PATCH 07/34] More major upgrade prechecks (#2775) Skip when - it is a standby clusters - there is no master in the cluster --- pkg/cluster/majorversionupgrade.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index e8876dc49..ad431acc4 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -145,6 +145,11 @@ func (c *Cluster) majorVersionUpgrade() error { for i, pod := range pods { ps, _ := c.patroni.GetMemberData(&pod) + if ps.Role == "standby_leader" { + c.logger.Errorf("skipping major version upgrade for %s/%s standby cluster. Re-deploy standby cluster with the required Postgres version specified", c.Namespace, c.Name) + return nil + } + if ps.State != "running" { allRunning = false c.logger.Infof("identified non running pod, potentially skipping major version upgrade") @@ -156,6 +161,11 @@ func (c *Cluster) majorVersionUpgrade() error { } } + if masterPod == nil { + c.logger.Infof("no master in the cluster, skipping major version upgrade") + return nil + } + // Recheck version with newest data from Patroni if c.currentMajorVersion >= desiredVersion { if _, exists := c.ObjectMeta.Annotations[majorVersionUpgradeFailureAnnotation]; exists { // if failure annotation exists, remove it From bb733346823c49b90524a28975cb743442bddd68 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 16 Oct 2024 17:14:44 +0200 Subject: [PATCH 08/34] quote admin user to allow names with special characters (#2774) --- pkg/util/users/users.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/util/users/users.go b/pkg/util/users/users.go index 4d9a21f73..924d8390e 100644 --- a/pkg/util/users/users.go +++ b/pkg/util/users/users.go @@ -24,7 +24,7 @@ const ( doBlockStmt = `SET LOCAL synchronous_commit = 'local'; DO $$ BEGIN %s; END;$$;` passwordTemplate = "ENCRYPTED PASSWORD '%s'" inRoleTemplate = `IN ROLE %s` - adminTemplate = `ADMIN %s` + adminTemplate = `ADMIN "%s"` ) // DefaultUserSyncStrategy implements a user sync strategy that merges already existing database users From d21466dbc4d1afe627f8ea7f14c6945832991f8d Mon Sep 17 00:00:00 2001 From: Prasad Krishnan <97734766+prasadkris@users.noreply.github.com> Date: Wed, 16 Oct 2024 20:48:01 +0530 Subject: [PATCH 09/34] update clusterrole.yaml (#2762) * update clusterrole.yaml * Update charts/postgres-operator/templates/clusterrole.yaml --------- Co-authored-by: Felix Kunde --- charts/postgres-operator/templates/clusterrole.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/postgres-operator/templates/clusterrole.yaml b/charts/postgres-operator/templates/clusterrole.yaml index d88affa0d..1fd066fa5 100644 --- a/charts/postgres-operator/templates/clusterrole.yaml +++ b/charts/postgres-operator/templates/clusterrole.yaml @@ -140,8 +140,8 @@ rules: - delete - get - list -{{- if toString .Values.configKubernetes.storage_resize_mode | eq "pvc" }} - patch +{{- if toString .Values.configKubernetes.storage_resize_mode | eq "pvc" }} - update {{- end }} # to read existing PVs. Creation should be done via dynamic provisioning From f5e122e8ef734d89b9d9f67fc03b0c6446312f5f Mon Sep 17 00:00:00 2001 From: Motte <37443982+dmotte@users.noreply.github.com> Date: Wed, 16 Oct 2024 17:19:07 +0200 Subject: [PATCH 10/34] Fix resource constraints (#2735) * Add empty string cases to patterns for pod resources * Added empty strings test case * Restored k8sres.go and changed test to zeros * Updated validation pattern in manifests/operatorconfiguration.crd.yaml and pkg/apis/acid.zalan.do/v1/crds.go --- .../crds/operatorconfigurations.yaml | 16 ++++---- manifests/operatorconfiguration.crd.yaml | 16 ++++---- pkg/apis/acid.zalan.do/v1/crds.go | 16 ++++---- pkg/cluster/k8sres_test.go | 38 +++++++++++++++++++ 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 5edb7044f..0a1e74613 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -376,28 +376,28 @@ spec: properties: default_cpu_limit: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' default_cpu_request: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' default_memory_limit: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' default_memory_request: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' max_cpu_request: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' max_memory_request: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' min_cpu_limit: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' min_memory_limit: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' timeouts: type: object properties: diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index c2b0cf398..a7b1a7280 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -374,28 +374,28 @@ spec: properties: default_cpu_limit: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' default_cpu_request: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' default_memory_limit: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' default_memory_request: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' max_cpu_request: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' max_memory_request: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' min_cpu_limit: type: string - pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + pattern: '^(\d+m|\d+(\.\d{1,3})?)$|^$' min_memory_limit: type: string - pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$|^$' timeouts: type: object properties: diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index da88b0855..c5c4b2706 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1573,35 +1573,35 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ Properties: map[string]apiextv1.JSONSchemaProps{ "default_cpu_limit": { Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", }, "default_cpu_request": { Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", }, "default_memory_limit": { Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", }, "default_memory_request": { Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", }, "max_cpu_request": { Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", }, "max_memory_request": { Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", }, "min_cpu_limit": { Type: "string", - Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$", + Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$|^$", }, "min_memory_limit": { Type: "string", - Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$", + Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$|^$", }, }, }, diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 07c05962d..bea229dda 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -2993,6 +2993,44 @@ func TestGenerateResourceRequirements(t *testing.T) { ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, }, }, + { + subTest: "test generation of resources when min limits are all set to zero", + config: config.Config{ + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: clusterNameLabel, + DefaultCPURequest: "0", + DefaultCPULimit: "0", + MaxCPURequest: "0", + MinCPULimit: "0", + DefaultMemoryRequest: "0", + DefaultMemoryLimit: "0", + MaxMemoryRequest: "0", + MinMemoryLimit: "0", + PodRoleLabel: "spilo-role", + }, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("5m"), Memory: k8sutil.StringToPointer("5Mi")}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("5m"), Memory: k8sutil.StringToPointer("5Mi")}, + }, + }, { subTest: "test matchLimitsWithRequestsIfSmaller", config: config.Config{ From 002d0f94a1463c15500dbfa77ea42c48e5790e6b Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 17 Oct 2024 16:52:24 +0200 Subject: [PATCH 11/34] quote schema names in case they use special characters and remove strings.Builder (#2782) --- pkg/cluster/cluster.go | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index b510613bf..ce9768bd2 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -1390,18 +1390,18 @@ func (c *Cluster) initPreparedDatabaseRoles() error { preparedSchemas = map[string]acidv1.PreparedSchema{"data": {DefaultRoles: util.True()}} } - var searchPath strings.Builder - searchPath.WriteString(constants.DefaultSearchPath) + searchPathArr := []string{constants.DefaultSearchPath} for preparedSchemaName := range preparedSchemas { - searchPath.WriteString(", " + preparedSchemaName) + searchPathArr = append(searchPathArr, fmt.Sprintf("%q", preparedSchemaName)) } + searchPath := strings.Join(searchPathArr, ", ") // default roles per database - if err := c.initDefaultRoles(defaultRoles, "admin", preparedDbName, searchPath.String(), preparedDB.SecretNamespace); err != nil { + if err := c.initDefaultRoles(defaultRoles, "admin", preparedDbName, searchPath, preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default roles for database %s: %v", preparedDbName, err) } if preparedDB.DefaultUsers { - if err := c.initDefaultRoles(defaultUsers, "admin", preparedDbName, searchPath.String(), preparedDB.SecretNamespace); err != nil { + if err := c.initDefaultRoles(defaultUsers, "admin", preparedDbName, searchPath, preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default roles for database %s: %v", preparedDbName, err) } } @@ -1412,14 +1412,16 @@ func (c *Cluster) initPreparedDatabaseRoles() error { if err := c.initDefaultRoles(defaultRoles, preparedDbName+constants.OwnerRoleNameSuffix, preparedDbName+"_"+preparedSchemaName, - constants.DefaultSearchPath+", "+preparedSchemaName, preparedDB.SecretNamespace); err != nil { + fmt.Sprintf("%s, %q", constants.DefaultSearchPath, preparedSchemaName), + preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default roles for database schema %s: %v", preparedSchemaName, err) } if preparedSchema.DefaultUsers { if err := c.initDefaultRoles(defaultUsers, preparedDbName+constants.OwnerRoleNameSuffix, preparedDbName+"_"+preparedSchemaName, - constants.DefaultSearchPath+", "+preparedSchemaName, preparedDB.SecretNamespace); err != nil { + fmt.Sprintf("%s, %q", constants.DefaultSearchPath, preparedSchemaName), + preparedDB.SecretNamespace); err != nil { return fmt.Errorf("could not initialize default users for database schema %s: %v", preparedSchemaName, err) } } From 45e9227f559d469cbd270c17cc7596533d8675ae Mon Sep 17 00:00:00 2001 From: Martin Kucin <52672224+Mart-Kuc@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:11:22 +0100 Subject: [PATCH 12/34] fix(postgres-operator/deployment): Set 'nindent' to 8 for 'extraEnvs' (#2783) Co-authored-by: martin.kucin --- charts/postgres-operator/Chart.yaml | 2 +- charts/postgres-operator/templates/deployment.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index ae4723b0e..89b6dd15a 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: postgres-operator -version: 1.13.0 +version: 1.14.0 appVersion: 1.13.0 home: https://github.com/zalando/postgres-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes diff --git a/charts/postgres-operator/templates/deployment.yaml b/charts/postgres-operator/templates/deployment.yaml index ddc3f6a0a..abd66cfc8 100644 --- a/charts/postgres-operator/templates/deployment.yaml +++ b/charts/postgres-operator/templates/deployment.yaml @@ -54,7 +54,7 @@ spec: value: {{ template "postgres-operator.controllerID" . }} {{- end }} {{- if .Values.extraEnvs }} - {{- .Values.extraEnvs | toYaml | nindent 12 }} + {{- .Values.extraEnvs | toYaml | nindent 8 }} {{- end }} resources: {{ toYaml .Values.resources | indent 10 }} From 8231797efad61f6982b85ded1f2545687eb7cbfe Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 31 Oct 2024 14:08:50 +0100 Subject: [PATCH 13/34] add cluster field for PVCs (#2785) * add cluster field for PVCs * sync volumes on cluster creation * fully spell pvc in log messages --- docs/reference/operator_parameters.md | 2 +- pkg/cluster/cluster.go | 8 +++- pkg/cluster/resources.go | 17 +++----- pkg/cluster/volumes.go | 56 +++++++++++++++++---------- pkg/cluster/volumes_test.go | 2 +- 5 files changed, 51 insertions(+), 34 deletions(-) diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 5b1eb64c9..4d4d16cdb 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -366,7 +366,7 @@ configuration they are grouped under the `kubernetes` key. manifest. To keep secrets, set this option to `false`. The default is `true`. * **enable_persistent_volume_claim_deletion** - By default, the operator deletes PersistentVolumeClaims when removing the + By default, the operator deletes persistent volume claims when removing the Postgres cluster manifest, no matter if `persistent_volume_claim_retention_policy` on the statefulset is set to `retain`. To keep PVCs set this option to `false`. The default is `true`. diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index ce9768bd2..1a8d6f762 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -65,11 +65,11 @@ type kubeResources struct { PatroniConfigMaps map[string]*v1.ConfigMap Secrets map[types.UID]*v1.Secret Statefulset *appsv1.StatefulSet + VolumeClaims map[types.UID]*v1.PersistentVolumeClaim PodDisruptionBudget *policyv1.PodDisruptionBudget LogicalBackupJob *batchv1.CronJob Streams map[string]*zalandov1.FabricEventStream //Pods are treated separately - //PVCs are treated separately } // Cluster describes postgresql cluster @@ -140,6 +140,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres Endpoints: make(map[PostgresRole]*v1.Endpoints), PatroniEndpoints: make(map[string]*v1.Endpoints), PatroniConfigMaps: make(map[string]*v1.ConfigMap), + VolumeClaims: make(map[types.UID]*v1.PersistentVolumeClaim), Streams: make(map[string]*zalandov1.FabricEventStream)}, userSyncStrategy: users.DefaultUserSyncStrategy{ PasswordEncryption: passwordEncryption, @@ -363,6 +364,11 @@ func (c *Cluster) Create() (err error) { c.logger.Infof("pods are ready") c.eventRecorder.Event(c.GetReference(), v1.EventTypeNormal, "StatefulSet", "Pods are ready") + // sync volume may already transition volumes to gp3, if iops/throughput or type is specified + if err = c.syncVolumes(); err != nil { + return err + } + // sync resources created by Patroni if err = c.syncPatroniResources(); err != nil { c.logger.Warnf("Patroni resources not yet synced: %v", err) diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index 6879ab928..3f47328ee 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -39,8 +39,8 @@ func (c *Cluster) listResources() error { c.logger.Infof("found logical backup job: %q (uid: %q)", util.NameFromMeta(c.LogicalBackupJob.ObjectMeta), c.LogicalBackupJob.UID) } - for _, secret := range c.Secrets { - c.logger.Infof("found secret: %q (uid: %q) namespace: %s", util.NameFromMeta(secret.ObjectMeta), secret.UID, secret.ObjectMeta.Namespace) + for uid, secret := range c.Secrets { + c.logger.Infof("found secret: %q (uid: %q) namespace: %s", util.NameFromMeta(secret.ObjectMeta), uid, secret.ObjectMeta.Namespace) } for role, service := range c.Services { @@ -70,13 +70,8 @@ func (c *Cluster) listResources() error { c.logger.Infof("found pod: %q (uid: %q)", util.NameFromMeta(obj.ObjectMeta), obj.UID) } - pvcs, err := c.listPersistentVolumeClaims() - if err != nil { - return fmt.Errorf("could not get the list of PVCs: %v", err) - } - - for _, obj := range pvcs { - c.logger.Infof("found PVC: %q (uid: %q)", util.NameFromMeta(obj.ObjectMeta), obj.UID) + for uid, pvc := range c.VolumeClaims { + c.logger.Infof("found persistent volume claim: %q (uid: %q)", util.NameFromMeta(pvc.ObjectMeta), uid) } for role, poolerObjs := range c.ConnectionPooler { @@ -288,10 +283,10 @@ func (c *Cluster) deleteStatefulSet() error { if c.OpConfig.EnablePersistentVolumeClaimDeletion != nil && *c.OpConfig.EnablePersistentVolumeClaimDeletion { if err := c.deletePersistentVolumeClaims(); err != nil { - return fmt.Errorf("could not delete PersistentVolumeClaims: %v", err) + return fmt.Errorf("could not delete persistent volume claims: %v", err) } } else { - c.logger.Info("not deleting PersistentVolumeClaims because disabled in configuration") + c.logger.Info("not deleting persistent volume claims because disabled in configuration") } return nil diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index 3a9a37cc1..165c6c7a3 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -13,9 +13,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/zalando/postgres-operator/pkg/spec" - "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/constants" "github.com/zalando/postgres-operator/pkg/util/filesystems" + "github.com/zalando/postgres-operator/pkg/util/k8sutil" "github.com/zalando/postgres-operator/pkg/util/volumes" ) @@ -185,7 +185,7 @@ func (c *Cluster) syncVolumeClaims() error { if c.OpConfig.StorageResizeMode == "off" || c.OpConfig.StorageResizeMode == "ebs" { ignoreResize = true - c.logger.Debugf("Storage resize mode is set to %q. Skipping volume size sync of PVCs.", c.OpConfig.StorageResizeMode) + c.logger.Debugf("Storage resize mode is set to %q. Skipping volume size sync of persistent volume claims.", c.OpConfig.StorageResizeMode) } newSize, err := resource.ParseQuantity(c.Spec.Volume.Size) @@ -196,9 +196,10 @@ func (c *Cluster) syncVolumeClaims() error { pvcs, err := c.listPersistentVolumeClaims() if err != nil { - return fmt.Errorf("could not receive persistent volume claims: %v", err) + return fmt.Errorf("could not list persistent volume claims: %v", err) } for _, pvc := range pvcs { + c.VolumeClaims[pvc.UID] = &pvc needsUpdate := false currentSize := quantityToGigabyte(pvc.Spec.Resources.Requests[v1.ResourceStorage]) if !ignoreResize && currentSize != manifestSize { @@ -213,9 +214,11 @@ func (c *Cluster) syncVolumeClaims() error { if needsUpdate { c.logger.Infof("updating persistent volume claim definition for volume %q", pvc.Name) - if _, err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Update(context.TODO(), &pvc, metav1.UpdateOptions{}); err != nil { + updatedPvc, err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Update(context.TODO(), &pvc, metav1.UpdateOptions{}) + if err != nil { return fmt.Errorf("could not update persistent volume claim: %q", err) } + c.VolumeClaims[pvc.UID] = updatedPvc c.logger.Infof("successfully updated persistent volume claim %q", pvc.Name) } else { c.logger.Debugf("volume claim for volume %q do not require updates", pvc.Name) @@ -227,10 +230,11 @@ func (c *Cluster) syncVolumeClaims() error { if err != nil { return fmt.Errorf("could not form patch for the persistent volume claim for volume %q: %v", pvc.Name, err) } - _, err = c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Patch(context.TODO(), pvc.Name, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) + patchedPvc, err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Patch(context.TODO(), pvc.Name, types.MergePatchType, []byte(patchData), metav1.PatchOptions{}) if err != nil { return fmt.Errorf("could not patch annotations of the persistent volume claim for volume %q: %v", pvc.Name, err) } + c.VolumeClaims[pvc.UID] = patchedPvc } } @@ -268,38 +272,50 @@ func (c *Cluster) listPersistentVolumeClaims() ([]v1.PersistentVolumeClaim, erro pvcs, err := c.KubeClient.PersistentVolumeClaims(ns).List(context.TODO(), listOptions) if err != nil { - return nil, fmt.Errorf("could not list of PersistentVolumeClaims: %v", err) + return nil, fmt.Errorf("could not list of persistent volume claims: %v", err) } return pvcs.Items, nil } func (c *Cluster) deletePersistentVolumeClaims() error { - c.logger.Debug("deleting PVCs") - pvcs, err := c.listPersistentVolumeClaims() - if err != nil { - return err - } - for _, pvc := range pvcs { - c.logger.Debugf("deleting PVC %q", util.NameFromMeta(pvc.ObjectMeta)) - if err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Delete(context.TODO(), pvc.Name, c.deleteOptions); err != nil { - c.logger.Warningf("could not delete PersistentVolumeClaim: %v", err) + c.setProcessName("deleting persistent volume claims") + errors := make([]string, 0) + for uid := range c.VolumeClaims { + err := c.deletePersistentVolumeClaim(uid) + if err != nil { + errors = append(errors, fmt.Sprintf("%v", err)) } } - if len(pvcs) > 0 { - c.logger.Debug("PVCs have been deleted") - } else { - c.logger.Debug("no PVCs to delete") + + if len(errors) > 0 { + c.logger.Warningf("could not delete all persistent volume claims: %v", strings.Join(errors, `', '`)) } return nil } +func (c *Cluster) deletePersistentVolumeClaim(uid types.UID) error { + c.setProcessName("deleting persistent volume claim") + pvc := c.VolumeClaims[uid] + c.logger.Debugf("deleting persistent volume claim %q", pvc.Name) + err := c.KubeClient.PersistentVolumeClaims(pvc.Namespace).Delete(context.TODO(), pvc.Name, c.deleteOptions) + if k8sutil.ResourceNotFound(err) { + c.logger.Debugf("persistent volume claim %q has already been deleted", pvc.Name) + } else if err != nil { + return fmt.Errorf("could not delete persistent volume claim %q: %v", pvc.Name, err) + } + c.logger.Infof("persistent volume claim %q has been deleted", pvc.Name) + delete(c.VolumeClaims, uid) + + return nil +} + func (c *Cluster) listPersistentVolumes() ([]*v1.PersistentVolume, error) { result := make([]*v1.PersistentVolume, 0) pvcs, err := c.listPersistentVolumeClaims() if err != nil { - return nil, fmt.Errorf("could not list cluster's PersistentVolumeClaims: %v", err) + return nil, fmt.Errorf("could not list cluster's persistent volume claims: %v", err) } pods, err := c.listPods() diff --git a/pkg/cluster/volumes_test.go b/pkg/cluster/volumes_test.go index 76b02e02e..99780982f 100644 --- a/pkg/cluster/volumes_test.go +++ b/pkg/cluster/volumes_test.go @@ -93,7 +93,7 @@ func TestResizeVolumeClaim(t *testing.T) { // check if listPersistentVolumeClaims returns only the PVCs matching the filter if len(pvcs) != len(pvcList.Items)-1 { - t.Errorf("%s: could not find all PVCs, got %v, expected %v", testName, len(pvcs), len(pvcList.Items)-1) + t.Errorf("%s: could not find all persistent volume claims, got %v, expected %v", testName, len(pvcs), len(pvcList.Items)-1) } // check if PVCs were correctly resized From acdb957d8ebf6ba784f72cd545af40def6973791 Mon Sep 17 00:00:00 2001 From: fahed dorgaa Date: Fri, 1 Nov 2024 17:06:20 +0100 Subject: [PATCH 14/34] fix switch over candidate retrieving (#2760) * fix switch over candidate retrieving Signed-off-by: fahed dorgaa --------- Signed-off-by: fahed dorgaa Co-authored-by: fahed dorgaa Co-authored-by: Felix Kunde --- pkg/cluster/pod.go | 33 +++++++++++++++------------------ pkg/cluster/pod_test.go | 4 ++-- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index 890b60122..bd2172c18 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -480,6 +480,9 @@ func (c *Cluster) getSwitchoverCandidate(master *v1.Pod) (spec.NamespacedName, e if PostgresRole(member.Role) == SyncStandby { syncCandidates = append(syncCandidates, member) } + if PostgresRole(member.Role) != Leader && PostgresRole(member.Role) != StandbyLeader && slices.Contains([]string{"running", "streaming", "in archive recovery"}, member.State) { + candidates = append(candidates, member) + } } // if synchronous mode is enabled and no SyncStandy was found @@ -489,6 +492,12 @@ func (c *Cluster) getSwitchoverCandidate(master *v1.Pod) (spec.NamespacedName, e return false, nil } + // retry also in asynchronous mode when no replica candidate was found + if !c.Spec.Patroni.SynchronousMode && len(candidates) == 0 { + c.logger.Warnf("no replica candidate found - retrying fetching cluster members") + return false, nil + } + return true, nil }, ) @@ -502,24 +511,12 @@ func (c *Cluster) getSwitchoverCandidate(master *v1.Pod) (spec.NamespacedName, e return syncCandidates[i].Lag < syncCandidates[j].Lag }) return spec.NamespacedName{Namespace: master.Namespace, Name: syncCandidates[0].Name}, nil - } else { - // in asynchronous mode find running replicas - for _, member := range members { - if PostgresRole(member.Role) == Leader || PostgresRole(member.Role) == StandbyLeader { - continue - } - - if slices.Contains([]string{"running", "streaming", "in archive recovery"}, member.State) { - candidates = append(candidates, member) - } - } - - if len(candidates) > 0 { - sort.Slice(candidates, func(i, j int) bool { - return candidates[i].Lag < candidates[j].Lag - }) - return spec.NamespacedName{Namespace: master.Namespace, Name: candidates[0].Name}, nil - } + } + if len(candidates) > 0 { + sort.Slice(candidates, func(i, j int) bool { + return candidates[i].Lag < candidates[j].Lag + }) + return spec.NamespacedName{Namespace: master.Namespace, Name: candidates[0].Name}, nil } return spec.NamespacedName{}, fmt.Errorf("no switchover candidate found") diff --git a/pkg/cluster/pod_test.go b/pkg/cluster/pod_test.go index e64e7eee3..6816b4d7a 100644 --- a/pkg/cluster/pod_test.go +++ b/pkg/cluster/pod_test.go @@ -62,7 +62,7 @@ func TestGetSwitchoverCandidate(t *testing.T) { expectedError: nil, }, { - subtest: "choose first replica when lag is equal evrywhere", + subtest: "choose first replica when lag is equal everywhere", clusterJson: `{"members": [{"name": "acid-test-cluster-0", "role": "leader", "state": "running", "api_url": "http://192.168.100.1:8008/patroni", "host": "192.168.100.1", "port": 5432, "timeline": 1}, {"name": "acid-test-cluster-1", "role": "replica", "state": "streaming", "api_url": "http://192.168.100.2:8008/patroni", "host": "192.168.100.2", "port": 5432, "timeline": 1, "lag": 5}, {"name": "acid-test-cluster-2", "role": "replica", "state": "running", "api_url": "http://192.168.100.3:8008/patroni", "host": "192.168.100.3", "port": 5432, "timeline": 1, "lag": 5}]}`, syncModeEnabled: false, expectedCandidate: spec.NamespacedName{Namespace: namespace, Name: "acid-test-cluster-1"}, @@ -73,7 +73,7 @@ func TestGetSwitchoverCandidate(t *testing.T) { clusterJson: `{"members": [{"name": "acid-test-cluster-0", "role": "leader", "state": "running", "api_url": "http://192.168.100.1:8008/patroni", "host": "192.168.100.1", "port": 5432, "timeline": 2}, {"name": "acid-test-cluster-1", "role": "replica", "state": "starting", "api_url": "http://192.168.100.2:8008/patroni", "host": "192.168.100.2", "port": 5432, "timeline": 2}]}`, syncModeEnabled: false, expectedCandidate: spec.NamespacedName{}, - expectedError: fmt.Errorf("no switchover candidate found"), + expectedError: fmt.Errorf("failed to get Patroni cluster members: unexpected end of JSON input"), }, { subtest: "replicas with different status", From c206eb38a80853c7dbc989875565dad938cb82c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:46:53 +0100 Subject: [PATCH 15/34] Bump werkzeug from 3.0.3 to 3.0.6 in /ui (#2793) Bumps [werkzeug](https://github.com/pallets/werkzeug) from 3.0.3 to 3.0.6. - [Release notes](https://github.com/pallets/werkzeug/releases) - [Changelog](https://github.com/pallets/werkzeug/blob/main/CHANGES.rst) - [Commits](https://github.com/pallets/werkzeug/compare/3.0.3...3.0.6) --- updated-dependencies: - dependency-name: werkzeug dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ui/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/requirements.txt b/ui/requirements.txt index edd649d2a..d3318ceec 100644 --- a/ui/requirements.txt +++ b/ui/requirements.txt @@ -12,4 +12,4 @@ python-json-logger==2.0.7 requests==2.32.2 stups-tokens>=1.1.19 wal_e==1.1.1 -werkzeug==3.0.3 +werkzeug==3.0.6 From fc9a26040a995739b75aabf85ab1ff26ec640d88 Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Mon, 16 Dec 2024 11:11:22 +0100 Subject: [PATCH 16/34] Integrate spilo with Patroni 4 (#2818) --- pkg/cluster/majorversionupgrade.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index ad431acc4..560f8977f 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -155,7 +155,7 @@ func (c *Cluster) majorVersionUpgrade() error { c.logger.Infof("identified non running pod, potentially skipping major version upgrade") } - if ps.Role == "master" { + if ps.Role == "master" || ps.Role == "primary" { masterPod = &pods[i] c.currentMajorVersion = ps.ServerVersion } From 4929dd204cca58fa623c8c2aa63732d95d215438 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 16 Dec 2024 11:22:40 +0100 Subject: [PATCH 17/34] Update major version upgrade docs (#2807) * Update major version upgrade logs --- docs/administrator.md | 46 ++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 14 deletions(-) diff --git a/docs/administrator.md b/docs/administrator.md index 86ceca291..725e93716 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -63,14 +63,17 @@ the `PGVERSION` environment variable is set for the database pods. Since `v1.6.0` the related option `enable_pgversion_env_var` is enabled by default. In-place major version upgrades can be configured to be executed by the -operator with the `major_version_upgrade_mode` option. By default it is set -to `off` which means the cluster version will not change when increased in -the manifest. Still, a rolling update would be triggered updating the -`PGVERSION` variable. But Spilo's [`configure_spilo`](https://github.com/zalando/spilo/blob/master/postgres-appliance/scripts/configure_spilo.py) -script will notice the version mismatch and start the old version again. +operator with the `major_version_upgrade_mode` option. By default, it is +enabled (mode: `manual`). In any case, altering the version in the manifest +will trigger a rolling update of pods to update the `PGVERSION` env variable. +Spilo's [`configure_spilo`](https://github.com/zalando/spilo/blob/master/postgres-appliance/scripts/configure_spilo.py) +script will notice the version mismatch but start the current version again. -In this scenario the major version could then be run by a user from within the -primary pod. Exec into the container and run: +Next, the operator would call an updage script inside Spilo. When automatic +upgrades are disabled (mode: `off`) the upgrade could still be run by a user +from within the primary pod. This gives you full control about the point in +time when the upgrade can be started (check also maintenance windows below). +Exec into the container and run: ```bash python3 /scripts/inplace_upgrade.py N ``` @@ -79,17 +82,32 @@ The upgrade is usually fast, well under one minute for most DBs. Note, that changes become irrevertible once `pg_upgrade` is called. To understand the upgrade procedure, refer to the [corresponding PR in Spilo](https://github.com/zalando/spilo/pull/488). -When `major_version_upgrade_mode` is set to `manual` the operator will run -the upgrade script for you after the manifest is updated and pods are rotated. -It is also possible to define `maintenanceWindows` in the Postgres manifest to -better control when such automated upgrades should take place after increasing -the version. +When `major_version_upgrade_mode` is set to `full` the operator will compare +the version in the manifest with the configured `minimal_major_version`. If it +is lower the operator would start an automatic upgrade as described above. The +configured `major_target_version` will be used as the new version. This option +can be useful if you have to get rid of outdated major versions in your fleet. +Please note, that the operator does not patch the version in the manifest. +Thus, the `full` mode can create drift between desired and actual state. + +### Upgrade during maintenance windows + +When `maintenanceWindows` are defined in the Postgres manifest the operator +will trigger a major version upgrade only during these periods. Make sure they +are at least twice as long as your configured `resync_period` to guarantee +that operator actions can be triggered. ### Upgrade annotations -When an upgrade is executed, the operator sets an annotation in the PostgreSQL resource, either `last-major-upgrade-success` if the upgrade succeeds, or `last-major-upgrade-failure` if it fails. The value of the annotation is a timestamp indicating when the upgrade occurred. +When an upgrade is executed, the operator sets an annotation in the PostgreSQL +resource, either `last-major-upgrade-success` if the upgrade succeeds, or +`last-major-upgrade-failure` if it fails. The value of the annotation is a +timestamp indicating when the upgrade occurred. -If a PostgreSQL resource contains a failure annotation, the operator will not attempt to retry the upgrade during a sync event. To remove the failure annotation, you can revert the PostgreSQL version back to the current version. This action will trigger the removal of the failure annotation. +If a PostgreSQL resource contains a failure annotation, the operator will not +attempt to retry the upgrade during a sync event. To remove the failure +annotation, you can revert the PostgreSQL version back to the current version. +This action will trigger the removal of the failure annotation. ## Non-default cluster domain From 301462c415697138a21e363d38bcb3bd08d39f20 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 16 Dec 2024 18:13:52 +0100 Subject: [PATCH 18/34] remove streams delete and extend unit tests (#2737) --- pkg/cluster/streams.go | 10 +-- pkg/cluster/streams_test.go | 164 +++++++++++++++++++++++------------- 2 files changed, 108 insertions(+), 66 deletions(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 3d9cbae11..6e940820d 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -453,15 +453,6 @@ func (c *Cluster) syncStream(appId string) error { if stream.Spec.ApplicationId != appId { continue } - if streamExists { - c.logger.Warningf("more than one event stream with applicationId %s found, delete it", appId) - if err = c.KubeClient.FabricEventStreams(stream.ObjectMeta.Namespace).Delete(context.TODO(), stream.ObjectMeta.Name, metav1.DeleteOptions{}); err != nil { - c.logger.Errorf("could not delete event stream %q with applicationId %s: %v", stream.ObjectMeta.Name, appId, err) - } else { - c.logger.Infof("redundant event stream %q with applicationId %s has been successfully deleted", stream.ObjectMeta.Name, appId) - } - continue - } streamExists = true desiredStreams := c.generateFabricEventStream(appId) if !reflect.DeepEqual(stream.ObjectMeta.OwnerReferences, desiredStreams.ObjectMeta.OwnerReferences) { @@ -484,6 +475,7 @@ func (c *Cluster) syncStream(appId string) error { c.Streams[appId] = updatedStream c.logger.Infof("event streams %q with applicationId %s have been successfully updated", updatedStream.Name, appId) } + break } if !streamExists { diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 92d28663e..77710aa19 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -90,7 +90,7 @@ var ( Namespace: namespace, Labels: map[string]string{ "application": "spilo", - "cluster-name": fmt.Sprintf("%s-2", clusterName), + "cluster-name": clusterName, "team": "acid", }, OwnerReferences: []metav1.OwnerReference{ @@ -494,14 +494,13 @@ func TestSyncStreams(t *testing.T) { OpConfig: config.Config{ PodManagementPolicy: "ordered_ready", Resources: config.Resources{ - ClusterLabels: map[string]string{"application": "spilo"}, - ClusterNameLabel: "cluster-name", - DefaultCPURequest: "300m", - DefaultCPULimit: "300m", - DefaultMemoryRequest: "300Mi", - DefaultMemoryLimit: "300Mi", - EnableOwnerReferences: util.True(), - PodRoleLabel: "spilo-role", + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + PodRoleLabel: "spilo-role", }, }, }, client, pg, logger, eventRecorder) @@ -514,33 +513,17 @@ func TestSyncStreams(t *testing.T) { err = cluster.syncStream(appId) assert.NoError(t, err) - // create a second stream with same spec but with different name - createdStream, err := cluster.KubeClient.FabricEventStreams(namespace).Create( - context.TODO(), fes, metav1.CreateOptions{}) + // sync the stream again + err = cluster.syncStream(appId) assert.NoError(t, err) - assert.Equal(t, createdStream.Spec.ApplicationId, appId) - // check that two streams exist + // check that only one stream remains after sync listOptions := metav1.ListOptions{ LabelSelector: cluster.labelsSet(true).String(), } streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) - assert.Equalf(t, 2, len(streams.Items), "unexpected number of streams found: got %d, but expected only 2", len(streams.Items)) - - // sync the stream which should remove the redundant stream - err = cluster.syncStream(appId) - assert.NoError(t, err) - - // check that only one stream remains after sync - streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) - assert.NoError(t, err) assert.Equalf(t, 1, len(streams.Items), "unexpected number of streams found: got %d, but expected only 1", len(streams.Items)) - - // check owner references - if !reflect.DeepEqual(streams.Items[0].OwnerReferences, cluster.ownerReferences()) { - t.Errorf("unexpected owner references, expected %#v, got %#v", cluster.ownerReferences(), streams.Items[0].OwnerReferences) - } } func TestSameStreams(t *testing.T) { @@ -663,13 +646,14 @@ func TestUpdateStreams(t *testing.T) { OpConfig: config.Config{ PodManagementPolicy: "ordered_ready", Resources: config.Resources{ - ClusterLabels: map[string]string{"application": "spilo"}, - ClusterNameLabel: "cluster-name", - DefaultCPURequest: "300m", - DefaultCPULimit: "300m", - DefaultMemoryRequest: "300Mi", - DefaultMemoryLimit: "300Mi", - PodRoleLabel: "spilo-role", + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + EnableOwnerReferences: util.True(), + PodRoleLabel: "spilo-role", }, }, }, client, pg, logger, eventRecorder) @@ -678,10 +662,31 @@ func TestUpdateStreams(t *testing.T) { context.TODO(), &pg, metav1.CreateOptions{}) assert.NoError(t, err) - // create the stream + // create stream with different owner reference + fes.ObjectMeta.Name = fmt.Sprintf("%s-12345", pg.Name) + fes.ObjectMeta.Labels["cluster-name"] = pg.Name + createdStream, err := cluster.KubeClient.FabricEventStreams(namespace).Create( + context.TODO(), fes, metav1.CreateOptions{}) + assert.NoError(t, err) + assert.Equal(t, createdStream.Spec.ApplicationId, appId) + + // sync the stream which should update the owner reference err = cluster.syncStream(appId) assert.NoError(t, err) + // check that only one stream exists after sync + listOptions := metav1.ListOptions{ + LabelSelector: cluster.labelsSet(true).String(), + } + streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) + assert.NoError(t, err) + assert.Equalf(t, 1, len(streams.Items), "unexpected number of streams found: got %d, but expected only 1", len(streams.Items)) + + // compare owner references + if !reflect.DeepEqual(streams.Items[0].OwnerReferences, cluster.ownerReferences()) { + t.Errorf("unexpected owner references, expected %#v, got %#v", cluster.ownerReferences(), streams.Items[0].OwnerReferences) + } + // change specs of streams and patch CRD for i, stream := range pg.Spec.Streams { if stream.ApplicationId == appId { @@ -694,10 +699,7 @@ func TestUpdateStreams(t *testing.T) { } // compare stream returned from API with expected stream - listOptions := metav1.ListOptions{ - LabelSelector: cluster.labelsSet(true).String(), - } - streams := patchPostgresqlStreams(t, cluster, &pg.Spec, listOptions) + streams = patchPostgresqlStreams(t, cluster, &pg.Spec, listOptions) result := cluster.generateFabricEventStream(appId) if match, _ := cluster.compareStreams(&streams.Items[0], result); !match { t.Errorf("Malformed FabricEventStream after updating manifest, expected %#v, got %#v", streams.Items[0], result) @@ -716,23 +718,6 @@ func TestUpdateStreams(t *testing.T) { if match, _ := cluster.compareStreams(&streams.Items[0], result); !match { t.Errorf("Malformed FabricEventStream after disabling event recovery, expected %#v, got %#v", streams.Items[0], result) } - - mockClient := k8sutil.NewMockKubernetesClient() - cluster.KubeClient.CustomResourceDefinitionsGetter = mockClient.CustomResourceDefinitionsGetter - - // remove streams from manifest - pg.Spec.Streams = nil - pgUpdated, err := cluster.KubeClient.Postgresqls(namespace).Update( - context.TODO(), &pg, metav1.UpdateOptions{}) - assert.NoError(t, err) - - appIds := getDistinctApplicationIds(pgUpdated.Spec.Streams) - cluster.cleanupRemovedStreams(appIds) - - streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) - if len(streams.Items) > 0 || err != nil { - t.Errorf("stream resource has not been removed or unexpected error %v", err) - } } func patchPostgresqlStreams(t *testing.T, cluster *Cluster, pgSpec *acidv1.PostgresSpec, listOptions metav1.ListOptions) (streams *zalandov1.FabricEventStreamList) { @@ -752,3 +737,68 @@ func patchPostgresqlStreams(t *testing.T, cluster *Cluster, pgSpec *acidv1.Postg return streams } + +func TestDeleteStreams(t *testing.T) { + pg.Name = fmt.Sprintf("%s-4", pg.Name) + var cluster = New( + Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + PodRoleLabel: "spilo-role", + }, + }, + }, client, pg, logger, eventRecorder) + + _, err := cluster.KubeClient.Postgresqls(namespace).Create( + context.TODO(), &pg, metav1.CreateOptions{}) + assert.NoError(t, err) + + // create the stream + err = cluster.syncStream(appId) + assert.NoError(t, err) + + // remove streams from manifest + pg.Spec.Streams = nil + pgUpdated, err := cluster.KubeClient.Postgresqls(namespace).Update( + context.TODO(), &pg, metav1.UpdateOptions{}) + assert.NoError(t, err) + + appIds := getDistinctApplicationIds(pgUpdated.Spec.Streams) + cluster.cleanupRemovedStreams(appIds) + + // check that streams have been deleted + listOptions := metav1.ListOptions{ + LabelSelector: cluster.labelsSet(true).String(), + } + streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) + assert.NoError(t, err) + assert.Equalf(t, 0, len(streams.Items), "unexpected number of streams found: got %d, but expected none", len(streams.Items)) + + // create stream to test deleteStreams code + fes.ObjectMeta.Name = fmt.Sprintf("%s-12345", pg.Name) + fes.ObjectMeta.Labels["cluster-name"] = pg.Name + _, err = cluster.KubeClient.FabricEventStreams(namespace).Create( + context.TODO(), fes, metav1.CreateOptions{}) + assert.NoError(t, err) + + // sync it once to cluster struct + err = cluster.syncStream(appId) + assert.NoError(t, err) + + // we need a mock client because deleteStreams checks for CRD existance + mockClient := k8sutil.NewMockKubernetesClient() + cluster.KubeClient.CustomResourceDefinitionsGetter = mockClient.CustomResourceDefinitionsGetter + cluster.deleteStreams() + + // check that streams have been deleted + streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) + assert.NoError(t, err) + assert.Equalf(t, 0, len(streams.Items), "unexpected number of streams found: got %d, but expected none", len(streams.Items)) +} From 80ef38f7f0fa2ddb8b60a239b354d70d59cf46dd Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 16 Dec 2024 18:17:19 +0100 Subject: [PATCH 19/34] add resource annotation and ignore recovery type (#2817) * add resource annotation and ignore recovery type * Update docs/reference/cluster_manifest.md --------- Co-authored-by: Ida Novindasari --- .../postgres-operator/crds/postgresqls.yaml | 8 +++ docs/reference/cluster_manifest.md | 30 ++++++++--- manifests/postgresql.crd.yaml | 8 +++ pkg/apis/acid.zalan.do/v1/postgresql_type.go | 3 ++ .../acid.zalan.do/v1/zz_generated.deepcopy.go | 15 ++++++ pkg/cluster/streams.go | 31 +++++++++-- pkg/cluster/streams_test.go | 52 ++++++++++++++++++- pkg/util/constants/streams.go | 27 +++++----- 8 files changed, 150 insertions(+), 24 deletions(-) diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index ebaf2d1f8..a83f7cc95 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -514,6 +514,9 @@ spec: type: string batchSize: type: integer + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' database: type: string enableRecovery: @@ -522,6 +525,9 @@ spec: type: object additionalProperties: type: string + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' tables: type: object additionalProperties: @@ -533,6 +539,8 @@ spec: type: string idColumn: type: string + ignoreRecovery: + type: boolean payloadColumn: type: string recoveryEventType: diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index bf731be2e..610982c73 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -652,11 +652,11 @@ can have the following properties: * **applicationId** The application name to which the database and CDC belongs to. For each - set of streams with a distinct `applicationId` a separate stream CR as well - as a separate logical replication slot will be created. This means there can - be different streams in the same database and streams with the same - `applicationId` are bundled in one stream CR. The stream CR will be called - like the Postgres cluster plus "-" suffix. Required. + set of streams with a distinct `applicationId` a separate stream resource as + well as a separate logical replication slot will be created. This means there + can be different streams in the same database and streams with the same + `applicationId` are bundled in one stream resource. The stream resource will + be called like the Postgres cluster plus "-" suffix. Required. * **database** Name of the database from where events will be published via Postgres' @@ -667,7 +667,8 @@ can have the following properties: * **tables** Defines a map of table names and their properties (`eventType`, `idColumn` - and `payloadColumn`). The CDC operator is following the [outbox pattern](https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/). + and `payloadColumn`). Required. + The CDC operator is following the [outbox pattern](https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/). The application is responsible for putting events into a (JSON/B or VARCHAR) payload column of the outbox table in the structure of the specified target event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/16/logical-replication-publication.html) @@ -676,12 +677,27 @@ can have the following properties: committed to the outbox table. The `idColumn` will be used in telemetry for the CDC operator. The names for `idColumn` and `payloadColumn` can be configured. Defaults are `id` and `payload`. The target `eventType` has to - be defined. Required. + be defined. One can also specify a `recoveryEventType` that will be used + for a dead letter queue. By enabling `ignoreRecovery`, you can choose to + ignore failing events. * **filter** Streamed events can be filtered by a jsonpath expression for each table. Optional. +* **enableRecovery** + Flag to enable a dead letter queue recovery for all streams tables. + Alternatively, recovery can also be enable for single outbox tables by only + specifying a `recoveryEventType` and no `enableRecovery` flag. When set to + false or missing, events will be retried until consuming succeeded. You can + use a `filter` expression to get rid of poison pills. Optional. + * **batchSize** Defines the size of batches in which events are consumed. Optional. Defaults to 1. + +* **cpu** + CPU requests to be set as an annotation on the stream resource. Optional. + +* **memory** + memory requests to be set as an annotation on the stream resource. Optional. diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 9207c83d4..9f7e3eff8 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -512,6 +512,9 @@ spec: type: string batchSize: type: integer + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' database: type: string enableRecovery: @@ -520,6 +523,9 @@ spec: type: object additionalProperties: type: string + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' tables: type: object additionalProperties: @@ -531,6 +537,8 @@ spec: type: string idColumn: type: string + ignoreRecovery: + type: boolean payloadColumn: type: string recoveryEventType: diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 3d731743f..1a8a311f5 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -258,6 +258,8 @@ type Stream struct { Tables map[string]StreamTable `json:"tables"` Filter map[string]*string `json:"filter,omitempty"` BatchSize *uint32 `json:"batchSize,omitempty"` + CPU *string `json:"cpu,omitempty"` + Memory *string `json:"memory,omitempty"` EnableRecovery *bool `json:"enableRecovery,omitempty"` } @@ -265,6 +267,7 @@ type Stream struct { type StreamTable struct { EventType string `json:"eventType"` RecoveryEventType string `json:"recoveryEventType,omitempty"` + IgnoreRecovery *bool `json:"ignoreRecovery,omitempty"` IdColumn *string `json:"idColumn,omitempty"` PayloadColumn *string `json:"payloadColumn,omitempty"` } diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 557f8889c..7c0b3ee23 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -1336,6 +1336,16 @@ func (in *Stream) DeepCopyInto(out *Stream) { *out = new(uint32) **out = **in } + if in.CPU != nil { + in, out := &in.CPU, &out.CPU + *out = new(string) + **out = **in + } + if in.Memory != nil { + in, out := &in.Memory, &out.Memory + *out = new(string) + **out = **in + } if in.EnableRecovery != nil { in, out := &in.EnableRecovery, &out.EnableRecovery *out = new(bool) @@ -1357,6 +1367,11 @@ func (in *Stream) DeepCopy() *Stream { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *StreamTable) DeepCopyInto(out *StreamTable) { *out = *in + if in.IgnoreRecovery != nil { + in, out := &in.IgnoreRecovery, &out.IgnoreRecovery + *out = new(bool) + **out = **in + } if in.IdColumn != nil { in, out := &in.IdColumn, &out.IdColumn *out = new(string) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 6e940820d..14fc3aaf0 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -178,16 +178,35 @@ func (c *Cluster) syncPublication(dbName string, databaseSlotsList map[string]za func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEventStream { eventStreams := make([]zalandov1.EventStream, 0) + resourceAnnotations := map[string]string{} for _, stream := range c.Spec.Streams { if stream.ApplicationId != appId { continue } + if stream.CPU != nil { + cpu, exists := resourceAnnotations[constants.EventStreamCpuAnnotationKey] + if exists { + isSmaller, _ := util.IsSmallerQuantity(cpu, *stream.CPU) + if isSmaller { + resourceAnnotations[constants.EventStreamCpuAnnotationKey] = *stream.CPU + } + } + } + if stream.Memory != nil { + memory, exists := resourceAnnotations[constants.EventStreamMemoryAnnotationKey] + if exists { + isSmaller, _ := util.IsSmallerQuantity(memory, *stream.Memory) + if isSmaller { + resourceAnnotations[constants.EventStreamMemoryAnnotationKey] = *stream.Memory + } + } + } for tableName, table := range stream.Tables { streamSource := c.getEventStreamSource(stream, tableName, table.IdColumn) streamFlow := getEventStreamFlow(table.PayloadColumn) streamSink := getEventStreamSink(stream, table.EventType) - streamRecovery := getEventStreamRecovery(stream, table.RecoveryEventType, table.EventType) + streamRecovery := getEventStreamRecovery(stream, table.RecoveryEventType, table.EventType, table.IgnoreRecovery) eventStreams = append(eventStreams, zalandov1.EventStream{ EventStreamFlow: streamFlow, @@ -207,7 +226,7 @@ func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEvent Name: fmt.Sprintf("%s-%s", c.Name, strings.ToLower(util.RandomPassword(5))), Namespace: c.Namespace, Labels: c.labelsSet(true), - Annotations: c.AnnotationsToPropagate(c.annotationsSet(nil)), + Annotations: c.AnnotationsToPropagate(c.annotationsSet(resourceAnnotations)), OwnerReferences: c.ownerReferences(), }, Spec: zalandov1.FabricEventStreamSpec{ @@ -247,7 +266,7 @@ func getEventStreamSink(stream acidv1.Stream, eventType string) zalandov1.EventS } } -func getEventStreamRecovery(stream acidv1.Stream, recoveryEventType, eventType string) zalandov1.EventStreamRecovery { +func getEventStreamRecovery(stream acidv1.Stream, recoveryEventType, eventType string, ignoreRecovery *bool) zalandov1.EventStreamRecovery { if (stream.EnableRecovery != nil && !*stream.EnableRecovery) || (stream.EnableRecovery == nil && recoveryEventType == "") { return zalandov1.EventStreamRecovery{ @@ -255,6 +274,12 @@ func getEventStreamRecovery(stream acidv1.Stream, recoveryEventType, eventType s } } + if ignoreRecovery != nil && *ignoreRecovery { + return zalandov1.EventStreamRecovery{ + Type: constants.EventStreamRecoveryIgnoreType, + } + } + if stream.EnableRecovery != nil && *stream.EnableRecovery && recoveryEventType == "" { recoveryEventType = fmt.Sprintf("%s-%s", eventType, constants.EventStreamRecoverySuffix) } diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 77710aa19..86fd235c7 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -65,12 +65,18 @@ var ( EventType: "stream-type-b", RecoveryEventType: "stream-type-b-dlq", }, + "data.foofoobar": { + EventType: "stream-type-c", + IgnoreRecovery: util.True(), + }, }, EnableRecovery: util.True(), Filter: map[string]*string{ "data.bar": k8sutil.StringToPointer("[?(@.source.txId > 500 && @.source.lsn > 123456)]"), }, BatchSize: k8sutil.UInt32ToPointer(uint32(100)), + CPU: k8sutil.StringToPointer("250m"), + Memory: k8sutil.StringToPointer("500Mi"), }, }, TeamID: "acid", @@ -88,6 +94,10 @@ var ( ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-12345", clusterName), Namespace: namespace, + Annotations: map[string]string{ + constants.EventStreamCpuAnnotationKey: "250m", + constants.EventStreamMemoryAnnotationKey: "500Mi", + }, Labels: map[string]string{ "application": "spilo", "cluster-name": clusterName, @@ -180,6 +190,37 @@ var ( Type: constants.EventStreamSourcePGType, }, }, + { + EventStreamFlow: zalandov1.EventStreamFlow{ + Type: constants.EventStreamFlowPgGenericType, + }, + EventStreamRecovery: zalandov1.EventStreamRecovery{ + Type: constants.EventStreamRecoveryIgnoreType, + }, + EventStreamSink: zalandov1.EventStreamSink{ + EventType: "stream-type-c", + MaxBatchSize: k8sutil.UInt32ToPointer(uint32(100)), + Type: constants.EventStreamSinkNakadiType, + }, + EventStreamSource: zalandov1.EventStreamSource{ + Connection: zalandov1.Connection{ + DBAuth: zalandov1.DBAuth{ + Name: fmt.Sprintf("fes-user.%s.credentials.postgresql.acid.zalan.do", clusterName), + PasswordKey: "password", + Type: constants.EventStreamSourceAuthType, + UserKey: "username", + }, + Url: fmt.Sprintf("jdbc:postgresql://%s.%s/foo?user=%s&ssl=true&sslmode=require", clusterName, namespace, fesUser), + SlotName: slotName, + PluginType: constants.EventStreamSourcePluginType, + }, + Schema: "data", + EventStreamTable: zalandov1.EventStreamTable{ + Name: "foofoobar", + }, + Type: constants.EventStreamSourcePGType, + }, + }, }, }, } @@ -528,8 +569,8 @@ func TestSyncStreams(t *testing.T) { func TestSameStreams(t *testing.T) { testName := "TestSameStreams" - annotationsA := map[string]string{"owned-by": "acid"} - annotationsB := map[string]string{"owned-by": "foo"} + annotationsA := map[string]string{constants.EventStreamMemoryAnnotationKey: "500Mi"} + annotationsB := map[string]string{constants.EventStreamMemoryAnnotationKey: "1Gi"} stream1 := zalandov1.EventStream{ EventStreamFlow: zalandov1.EventStreamFlow{}, @@ -621,6 +662,13 @@ func TestSameStreams(t *testing.T) { match: false, reason: "event stream specs differ", }, + { + subTest: "event stream annotations differ", + streamsA: newFabricEventStream([]zalandov1.EventStream{stream2}, nil), + streamsB: newFabricEventStream([]zalandov1.EventStream{stream3}, annotationsA), + match: false, + reason: "event stream specs differ", + }, { subTest: "event stream annotations differ", streamsA: newFabricEventStream([]zalandov1.EventStream{stream2}, annotationsA), diff --git a/pkg/util/constants/streams.go b/pkg/util/constants/streams.go index 8916701f3..cb4bb6a3f 100644 --- a/pkg/util/constants/streams.go +++ b/pkg/util/constants/streams.go @@ -2,16 +2,19 @@ package constants // PostgreSQL specific constants const ( - EventStreamCRDApiVersion = "zalando.org/v1" - EventStreamCRDKind = "FabricEventStream" - EventStreamCRDName = "fabriceventstreams.zalando.org" - EventStreamSourcePGType = "PostgresLogicalReplication" - EventStreamSourceSlotPrefix = "fes" - EventStreamSourcePluginType = "pgoutput" - EventStreamSourceAuthType = "DatabaseAuthenticationSecret" - EventStreamFlowPgGenericType = "PostgresWalToGenericNakadiEvent" - EventStreamSinkNakadiType = "Nakadi" - EventStreamRecoveryNoneType = "None" - EventStreamRecoveryDLQType = "DeadLetter" - EventStreamRecoverySuffix = "dead-letter-queue" + EventStreamCRDApiVersion = "zalando.org/v1" + EventStreamCRDKind = "FabricEventStream" + EventStreamCRDName = "fabriceventstreams.zalando.org" + EventStreamSourcePGType = "PostgresLogicalReplication" + EventStreamSourceSlotPrefix = "fes" + EventStreamSourcePluginType = "pgoutput" + EventStreamSourceAuthType = "DatabaseAuthenticationSecret" + EventStreamFlowPgGenericType = "PostgresWalToGenericNakadiEvent" + EventStreamSinkNakadiType = "Nakadi" + EventStreamRecoveryDLQType = "DeadLetter" + EventStreamRecoveryIgnoreType = "Ignore" + EventStreamRecoveryNoneType = "None" + EventStreamRecoverySuffix = "dead-letter-queue" + EventStreamCpuAnnotationKey = "fes.zalando.org/FES_CPU" + EventStreamMemoryAnnotationKey = "fes.zalando.org/FES_MEMORY" ) From d44bfabe786589c9dec4f498c41f801ea0664e7b Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Tue, 17 Dec 2024 08:54:37 +0100 Subject: [PATCH 20/34] do not use extra labels to list stream CRDs (#2803) * do not use extra labels to list stream CRDs * add diff on labels for streams + unit test coverage --- pkg/cluster/streams.go | 12 ++++++-- pkg/cluster/streams_test.go | 55 ++++++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 14fc3aaf0..616a6828e 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -467,7 +467,9 @@ func (c *Cluster) syncStream(appId string) error { c.setProcessName("syncing stream with applicationId %s", appId) c.logger.Debugf("syncing stream with applicationId %s", appId) - listOptions := metav1.ListOptions{LabelSelector: c.labelsSet(true).String()} + listOptions := metav1.ListOptions{ + LabelSelector: c.labelsSet(false).String(), + } streams, err = c.KubeClient.FabricEventStreams(c.Namespace).List(context.TODO(), listOptions) if err != nil { return fmt.Errorf("could not list of FabricEventStreams for applicationId %s: %v", appId, err) @@ -492,7 +494,8 @@ func (c *Cluster) syncStream(appId string) error { } if match, reason := c.compareStreams(&stream, desiredStreams); !match { c.logger.Infof("updating event streams with applicationId %s: %s", appId, reason) - desiredStreams.ObjectMeta = stream.ObjectMeta + // make sure to keep the old name with randomly generated suffix + desiredStreams.ObjectMeta.Name = stream.ObjectMeta.Name updatedStream, err := c.updateStreams(desiredStreams) if err != nil { return fmt.Errorf("failed updating event streams %s with applicationId %s: %v", stream.Name, appId, err) @@ -527,6 +530,11 @@ func (c *Cluster) compareStreams(curEventStreams, newEventStreams *zalandov1.Fab reasons = append(reasons, fmt.Sprintf("new streams annotations do not match: %s", reason)) } + if !reflect.DeepEqual(curEventStreams.ObjectMeta.Labels, newEventStreams.ObjectMeta.Labels) { + match = false + reasons = append(reasons, "new streams labels do not match the current ones") + } + if changed, reason := sameEventStreams(curEventStreams.Spec.EventStreams, newEventStreams.Spec.EventStreams); !changed { match = false reasons = append(reasons, fmt.Sprintf("new streams EventStreams array does not match : %s", reason)) diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index 86fd235c7..dac3615c8 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -490,7 +490,7 @@ func TestGenerateFabricEventStream(t *testing.T) { } listOptions := metav1.ListOptions{ - LabelSelector: cluster.labelsSet(true).String(), + LabelSelector: cluster.labelsSet(false).String(), } streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) @@ -529,7 +529,8 @@ func newFabricEventStream(streams []zalandov1.EventStream, annotations map[strin } func TestSyncStreams(t *testing.T) { - pg.Name = fmt.Sprintf("%s-2", pg.Name) + newClusterName := fmt.Sprintf("%s-2", pg.Name) + pg.Name = newClusterName var cluster = New( Config{ OpConfig: config.Config{ @@ -560,7 +561,7 @@ func TestSyncStreams(t *testing.T) { // check that only one stream remains after sync listOptions := metav1.ListOptions{ - LabelSelector: cluster.labelsSet(true).String(), + LabelSelector: cluster.labelsSet(false).String(), } streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) @@ -812,6 +813,49 @@ func TestDeleteStreams(t *testing.T) { err = cluster.syncStream(appId) assert.NoError(t, err) + // change specs of streams and patch CRD + for i, stream := range pg.Spec.Streams { + if stream.ApplicationId == appId { + streamTable := stream.Tables["data.bar"] + streamTable.EventType = "stream-type-c" + stream.Tables["data.bar"] = streamTable + stream.BatchSize = k8sutil.UInt32ToPointer(uint32(250)) + pg.Spec.Streams[i] = stream + } + } + + // compare stream returned from API with expected stream + listOptions := metav1.ListOptions{ + LabelSelector: cluster.labelsSet(false).String(), + } + streams := patchPostgresqlStreams(t, cluster, &pg.Spec, listOptions) + result := cluster.generateFabricEventStream(appId) + if match, _ := cluster.compareStreams(&streams.Items[0], result); !match { + t.Errorf("Malformed FabricEventStream after updating manifest, expected %#v, got %#v", streams.Items[0], result) + } + + // change teamId and check that stream is updated + pg.Spec.TeamID = "new-team" + streams = patchPostgresqlStreams(t, cluster, &pg.Spec, listOptions) + result = cluster.generateFabricEventStream(appId) + if match, _ := cluster.compareStreams(&streams.Items[0], result); !match { + t.Errorf("Malformed FabricEventStream after updating teamId, expected %#v, got %#v", streams.Items[0].ObjectMeta.Labels, result.ObjectMeta.Labels) + } + + // disable recovery + for idx, stream := range pg.Spec.Streams { + if stream.ApplicationId == appId { + stream.EnableRecovery = util.False() + pg.Spec.Streams[idx] = stream + } + } + + streams = patchPostgresqlStreams(t, cluster, &pg.Spec, listOptions) + result = cluster.generateFabricEventStream(appId) + if match, _ := cluster.compareStreams(&streams.Items[0], result); !match { + t.Errorf("Malformed FabricEventStream after disabling event recovery, expected %#v, got %#v", streams.Items[0], result) + } + // remove streams from manifest pg.Spec.Streams = nil pgUpdated, err := cluster.KubeClient.Postgresqls(namespace).Update( @@ -822,10 +866,7 @@ func TestDeleteStreams(t *testing.T) { cluster.cleanupRemovedStreams(appIds) // check that streams have been deleted - listOptions := metav1.ListOptions{ - LabelSelector: cluster.labelsSet(true).String(), - } - streams, err := cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) + streams, err = cluster.KubeClient.FabricEventStreams(namespace).List(context.TODO(), listOptions) assert.NoError(t, err) assert.Equalf(t, 0, len(streams.Items), "unexpected number of streams found: got %d, but expected none", len(streams.Items)) From 5450113eb56f1e9dd192a1a0aae48497623ff482 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:07:15 +0100 Subject: [PATCH 21/34] Bump golang.org/x/crypto from 0.26.0 to 0.31.0 (#2816) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index d6390f45f..760cd3fbf 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/r3labs/diff v1.1.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.9.0 - golang.org/x/crypto v0.26.0 + golang.org/x/crypto v0.31.0 golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.30.4 @@ -54,10 +54,10 @@ require ( golang.org/x/mod v0.17.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.10.0 // indirect - golang.org/x/sync v0.8.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sync v0.10.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect google.golang.org/appengine v1.6.7 // indirect diff --git a/go.sum b/go.sum index c7992fea0..0e55f2dd7 100644 --- a/go.sum +++ b/go.sum @@ -119,8 +119,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -142,8 +142,8 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -151,16 +151,16 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From 8cc679653782a210d874009838fafde1d2385676 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Wed, 18 Dec 2024 11:22:08 +0100 Subject: [PATCH 22/34] fix comparing stream annotations and improve unit test (#2820) --- e2e/tests/test_e2e.py | 4 ++- pkg/cluster/streams.go | 56 +++++++++++++++++++++++++------------ pkg/cluster/streams_test.go | 22 +++++++-------- 3 files changed, 52 insertions(+), 30 deletions(-) diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index f89e2fb86..f5a05a157 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -2204,6 +2204,8 @@ class EndToEndTestCase(unittest.TestCase): { "applicationId": "test-app", "batchSize": 100, + "cpu": "100m", + "memory": "200Mi", "database": "foo", "enableRecovery": True, "tables": { @@ -2225,7 +2227,7 @@ class EndToEndTestCase(unittest.TestCase): "eventType": "test-event", "idColumn": "id", "payloadColumn": "payload", - "recoveryEventType": "test-event-dlq" + "ignoreRecovery": True } } } diff --git a/pkg/cluster/streams.go b/pkg/cluster/streams.go index 616a6828e..9e2c7482a 100644 --- a/pkg/cluster/streams.go +++ b/pkg/cluster/streams.go @@ -179,29 +179,19 @@ func (c *Cluster) syncPublication(dbName string, databaseSlotsList map[string]za func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEventStream { eventStreams := make([]zalandov1.EventStream, 0) resourceAnnotations := map[string]string{} + var err, err2 error for _, stream := range c.Spec.Streams { if stream.ApplicationId != appId { continue } - if stream.CPU != nil { - cpu, exists := resourceAnnotations[constants.EventStreamCpuAnnotationKey] - if exists { - isSmaller, _ := util.IsSmallerQuantity(cpu, *stream.CPU) - if isSmaller { - resourceAnnotations[constants.EventStreamCpuAnnotationKey] = *stream.CPU - } - } - } - if stream.Memory != nil { - memory, exists := resourceAnnotations[constants.EventStreamMemoryAnnotationKey] - if exists { - isSmaller, _ := util.IsSmallerQuantity(memory, *stream.Memory) - if isSmaller { - resourceAnnotations[constants.EventStreamMemoryAnnotationKey] = *stream.Memory - } - } + + err = setResourceAnnotation(&resourceAnnotations, stream.CPU, constants.EventStreamCpuAnnotationKey) + err2 = setResourceAnnotation(&resourceAnnotations, stream.Memory, constants.EventStreamMemoryAnnotationKey) + if err != nil || err2 != nil { + c.logger.Warningf("could not set resource annotation for event stream: %v", err) } + for tableName, table := range stream.Tables { streamSource := c.getEventStreamSource(stream, tableName, table.IdColumn) streamFlow := getEventStreamFlow(table.PayloadColumn) @@ -236,6 +226,27 @@ func (c *Cluster) generateFabricEventStream(appId string) *zalandov1.FabricEvent } } +func setResourceAnnotation(annotations *map[string]string, resource *string, key string) error { + var ( + isSmaller bool + err error + ) + if resource != nil { + currentValue, exists := (*annotations)[key] + if exists { + isSmaller, err = util.IsSmallerQuantity(currentValue, *resource) + if err != nil { + return fmt.Errorf("could not compare resource in %q annotation: %v", key, err) + } + } + if isSmaller || !exists { + (*annotations)[key] = *resource + } + } + + return nil +} + func (c *Cluster) getEventStreamSource(stream acidv1.Stream, tableName string, idColumn *string) zalandov1.EventStreamSource { table, schema := getTableSchema(tableName) streamFilter := stream.Filter[tableName] @@ -521,10 +532,19 @@ func (c *Cluster) syncStream(appId string) error { func (c *Cluster) compareStreams(curEventStreams, newEventStreams *zalandov1.FabricEventStream) (match bool, reason string) { reasons := make([]string, 0) + desiredAnnotations := make(map[string]string) match = true // stream operator can add extra annotations so incl. current annotations in desired annotations - desiredAnnotations := c.annotationsSet(curEventStreams.Annotations) + for curKey, curValue := range curEventStreams.Annotations { + if _, exists := desiredAnnotations[curKey]; !exists { + desiredAnnotations[curKey] = curValue + } + } + // add/or override annotations if cpu and memory values were changed + for newKey, newValue := range newEventStreams.Annotations { + desiredAnnotations[newKey] = newValue + } if changed, reason := c.compareAnnotations(curEventStreams.ObjectMeta.Annotations, desiredAnnotations); changed { match = false reasons = append(reasons, fmt.Sprintf("new streams annotations do not match: %s", reason)) diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index dac3615c8..dd76a41f4 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -640,49 +640,49 @@ func TestSameStreams(t *testing.T) { streamsA: newFabricEventStream([]zalandov1.EventStream{stream1}, nil), streamsB: newFabricEventStream([]zalandov1.EventStream{stream1, stream2}, nil), match: false, - reason: "number of defined streams is different", + reason: "new streams EventStreams array does not match : number of defined streams is different", }, { subTest: "different number of streams", streamsA: newFabricEventStream([]zalandov1.EventStream{stream1}, nil), streamsB: newFabricEventStream([]zalandov1.EventStream{stream1, stream2}, nil), match: false, - reason: "number of defined streams is different", + reason: "new streams EventStreams array does not match : number of defined streams is different", }, { subTest: "event stream specs differ", streamsA: newFabricEventStream([]zalandov1.EventStream{stream1, stream2}, nil), streamsB: fes, match: false, - reason: "number of defined streams is different", + reason: "new streams annotations do not match: Added \"fes.zalando.org/FES_CPU\" with value \"250m\". Added \"fes.zalando.org/FES_MEMORY\" with value \"500Mi\"., new streams labels do not match the current ones, new streams EventStreams array does not match : number of defined streams is different", }, { subTest: "event stream recovery specs differ", streamsA: newFabricEventStream([]zalandov1.EventStream{stream2}, nil), streamsB: newFabricEventStream([]zalandov1.EventStream{stream3}, nil), match: false, - reason: "event stream specs differ", + reason: "new streams EventStreams array does not match : event stream specs differ", }, { - subTest: "event stream annotations differ", + subTest: "event stream with new annotations", streamsA: newFabricEventStream([]zalandov1.EventStream{stream2}, nil), - streamsB: newFabricEventStream([]zalandov1.EventStream{stream3}, annotationsA), + streamsB: newFabricEventStream([]zalandov1.EventStream{stream2}, annotationsA), match: false, - reason: "event stream specs differ", + reason: "new streams annotations do not match: Added \"fes.zalando.org/FES_MEMORY\" with value \"500Mi\".", }, { subTest: "event stream annotations differ", - streamsA: newFabricEventStream([]zalandov1.EventStream{stream2}, annotationsA), + streamsA: newFabricEventStream([]zalandov1.EventStream{stream3}, annotationsA), streamsB: newFabricEventStream([]zalandov1.EventStream{stream3}, annotationsB), match: false, - reason: "event stream specs differ", + reason: "new streams annotations do not match: \"fes.zalando.org/FES_MEMORY\" changed from \"500Mi\" to \"1Gi\".", }, } for _, tt := range tests { streamsMatch, matchReason := cluster.compareStreams(tt.streamsA, tt.streamsB) - if streamsMatch != tt.match { - t.Errorf("%s %s: unexpected match result when comparing streams: got %s, epxected %s", + if streamsMatch != tt.match || matchReason != tt.reason { + t.Errorf("%s %s: unexpected match result when comparing streams: got %s, expected %s", testName, tt.subTest, matchReason, tt.reason) } } From e7cc4f9120618ee8d3c9fb1a040544a2897d7bff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:03:20 +0100 Subject: [PATCH 23/34] Bump golang.org/x/crypto from 0.26.0 to 0.31.0 in /kubectl-pg (#2819) Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.26.0 to 0.31.0. - [Commits](https://github.com/golang/crypto/compare/v0.26.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- kubectl-pg/go.mod | 8 ++++---- kubectl-pg/go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 67c83354b..036a48bdc 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -51,13 +51,13 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/subosito/gotenv v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.26.0 // indirect + golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.18.0 // indirect - golang.org/x/sys v0.23.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/term v0.27.0 // indirect + golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.5.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/protobuf v1.33.0 // indirect diff --git a/kubectl-pg/go.sum b/kubectl-pg/go.sum index c873d0e37..2237a9e03 100644 --- a/kubectl-pg/go.sum +++ b/kubectl-pg/go.sum @@ -137,8 +137,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -166,18 +166,18 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From eef49500a532ac8afee36a7ae5e0e970f408b1a4 Mon Sep 17 00:00:00 2001 From: cosimomeli Date: Thu, 19 Dec 2024 12:32:09 +0100 Subject: [PATCH 24/34] Add support for EBS CSI Driver (#2677) * Add support for EBS CSI Driver --- .gitignore | 2 + .../templates/clusterrole.yaml | 2 +- pkg/cluster/volumes.go | 2 +- pkg/cluster/volumes_test.go | 24 ++++ pkg/util/constants/aws.go | 1 + pkg/util/volumes/ebs.go | 10 +- pkg/util/volumes/ebs_test.go | 123 ++++++++++++++++++ 7 files changed, 160 insertions(+), 4 deletions(-) create mode 100644 pkg/util/volumes/ebs_test.go diff --git a/.gitignore b/.gitignore index 66a8103d0..5938db216 100644 --- a/.gitignore +++ b/.gitignore @@ -104,3 +104,5 @@ e2e/tls mocks ui/.npm/ + +.DS_Store diff --git a/charts/postgres-operator/templates/clusterrole.yaml b/charts/postgres-operator/templates/clusterrole.yaml index 1fd066fa5..ad3b46064 100644 --- a/charts/postgres-operator/templates/clusterrole.yaml +++ b/charts/postgres-operator/templates/clusterrole.yaml @@ -141,7 +141,7 @@ rules: - get - list - patch -{{- if toString .Values.configKubernetes.storage_resize_mode | eq "pvc" }} +{{- if or (toString .Values.configKubernetes.storage_resize_mode | eq "pvc") (toString .Values.configKubernetes.storage_resize_mode | eq "mixed") }} - update {{- end }} # to read existing PVs. Creation should be done via dynamic provisioning diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index 165c6c7a3..240220ccf 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -151,7 +151,7 @@ func (c *Cluster) populateVolumeMetaData() error { volumeIds := []string{} var volumeID string for _, pv := range pvs { - volumeID, err = c.VolumeResizer.ExtractVolumeID(pv.Spec.AWSElasticBlockStore.VolumeID) + volumeID, err = c.VolumeResizer.GetProviderVolumeID(pv) if err != nil { continue } diff --git a/pkg/cluster/volumes_test.go b/pkg/cluster/volumes_test.go index 99780982f..95ecc7624 100644 --- a/pkg/cluster/volumes_test.go +++ b/pkg/cluster/volumes_test.go @@ -216,6 +216,12 @@ func TestMigrateEBS(t *testing.T) { resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-1")).Return("ebs-volume-1", nil) resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-2")).Return("ebs-volume-2", nil) + resizer.EXPECT().GetProviderVolumeID(gomock.Any()). + DoAndReturn(func(pv *v1.PersistentVolume) (string, error) { + return resizer.ExtractVolumeID(pv.Spec.AWSElasticBlockStore.VolumeID) + }). + Times(2) + resizer.EXPECT().DescribeVolumes(gomock.Eq([]string{"ebs-volume-1", "ebs-volume-2"})).Return( []volumes.VolumeProperties{ {VolumeID: "ebs-volume-1", VolumeType: "gp2", Size: 100}, @@ -322,6 +328,12 @@ func TestMigrateGp3Support(t *testing.T) { resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-2")).Return("ebs-volume-2", nil) resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-3")).Return("ebs-volume-3", nil) + resizer.EXPECT().GetProviderVolumeID(gomock.Any()). + DoAndReturn(func(pv *v1.PersistentVolume) (string, error) { + return resizer.ExtractVolumeID(pv.Spec.AWSElasticBlockStore.VolumeID) + }). + Times(3) + resizer.EXPECT().DescribeVolumes(gomock.Eq([]string{"ebs-volume-1", "ebs-volume-2", "ebs-volume-3"})).Return( []volumes.VolumeProperties{ {VolumeID: "ebs-volume-1", VolumeType: "gp3", Size: 100, Iops: 3000}, @@ -377,6 +389,12 @@ func TestManualGp2Gp3Support(t *testing.T) { resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-1")).Return("ebs-volume-1", nil) resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-2")).Return("ebs-volume-2", nil) + resizer.EXPECT().GetProviderVolumeID(gomock.Any()). + DoAndReturn(func(pv *v1.PersistentVolume) (string, error) { + return resizer.ExtractVolumeID(pv.Spec.AWSElasticBlockStore.VolumeID) + }). + Times(2) + resizer.EXPECT().DescribeVolumes(gomock.Eq([]string{"ebs-volume-1", "ebs-volume-2"})).Return( []volumes.VolumeProperties{ {VolumeID: "ebs-volume-1", VolumeType: "gp2", Size: 150, Iops: 3000}, @@ -436,6 +454,12 @@ func TestDontTouchType(t *testing.T) { resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-1")).Return("ebs-volume-1", nil) resizer.EXPECT().ExtractVolumeID(gomock.Eq("aws://eu-central-1b/ebs-volume-2")).Return("ebs-volume-2", nil) + resizer.EXPECT().GetProviderVolumeID(gomock.Any()). + DoAndReturn(func(pv *v1.PersistentVolume) (string, error) { + return resizer.ExtractVolumeID(pv.Spec.AWSElasticBlockStore.VolumeID) + }). + Times(2) + resizer.EXPECT().DescribeVolumes(gomock.Eq([]string{"ebs-volume-1", "ebs-volume-2"})).Return( []volumes.VolumeProperties{ {VolumeID: "ebs-volume-1", VolumeType: "gp2", Size: 150, Iops: 3000}, diff --git a/pkg/util/constants/aws.go b/pkg/util/constants/aws.go index f1cfd5975..147e58889 100644 --- a/pkg/util/constants/aws.go +++ b/pkg/util/constants/aws.go @@ -7,6 +7,7 @@ const ( // EBS related constants EBSVolumeIDStart = "/vol-" EBSProvisioner = "kubernetes.io/aws-ebs" + EBSDriver = "ebs.csi.aws.com" //https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_VolumeModification.html EBSVolumeStateModifying = "modifying" EBSVolumeStateOptimizing = "optimizing" diff --git a/pkg/util/volumes/ebs.go b/pkg/util/volumes/ebs.go index f625dab2f..cb8f8e97f 100644 --- a/pkg/util/volumes/ebs.go +++ b/pkg/util/volumes/ebs.go @@ -36,7 +36,8 @@ func (r *EBSVolumeResizer) IsConnectedToProvider() bool { // VolumeBelongsToProvider checks if the given persistent volume is backed by EBS. func (r *EBSVolumeResizer) VolumeBelongsToProvider(pv *v1.PersistentVolume) bool { - return pv.Spec.AWSElasticBlockStore != nil && pv.Annotations[constants.VolumeStorateProvisionerAnnotation] == constants.EBSProvisioner + return (pv.Spec.AWSElasticBlockStore != nil && pv.Annotations[constants.VolumeStorateProvisionerAnnotation] == constants.EBSProvisioner) || + (pv.Spec.CSI != nil && pv.Spec.CSI.Driver == constants.EBSDriver) } // ExtractVolumeID extracts volumeID from "aws://eu-central-1a/vol-075ddfc4a127d0bd4" @@ -54,7 +55,12 @@ func (r *EBSVolumeResizer) ExtractVolumeID(volumeID string) (string, error) { // GetProviderVolumeID converts aws://eu-central-1b/vol-00f93d4827217c629 to vol-00f93d4827217c629 for EBS volumes func (r *EBSVolumeResizer) GetProviderVolumeID(pv *v1.PersistentVolume) (string, error) { - volumeID := pv.Spec.AWSElasticBlockStore.VolumeID + var volumeID string = "" + if pv.Spec.CSI != nil { + volumeID = pv.Spec.CSI.VolumeHandle + } else if pv.Spec.AWSElasticBlockStore != nil { + volumeID = pv.Spec.AWSElasticBlockStore.VolumeID + } if volumeID == "" { return "", fmt.Errorf("got empty volume id for volume %v", pv) } diff --git a/pkg/util/volumes/ebs_test.go b/pkg/util/volumes/ebs_test.go new file mode 100644 index 000000000..6f722ff7b --- /dev/null +++ b/pkg/util/volumes/ebs_test.go @@ -0,0 +1,123 @@ +package volumes + +import ( + "fmt" + "testing" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestGetProviderVolumeID(t *testing.T) { + tests := []struct { + name string + pv *v1.PersistentVolume + expected string + err error + }{ + { + name: "CSI volume handle", + pv: &v1.PersistentVolume{ + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeSource: v1.PersistentVolumeSource{ + CSI: &v1.CSIPersistentVolumeSource{ + VolumeHandle: "vol-075ddfc4a127d0bd5", + }, + }, + }, + }, + expected: "vol-075ddfc4a127d0bd5", + err: nil, + }, + { + name: "AWS EBS volume handle", + pv: &v1.PersistentVolume{ + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeSource: v1.PersistentVolumeSource{ + AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws://eu-central-1a/vol-075ddfc4a127d0bd4", + }, + }, + }, + }, + expected: "vol-075ddfc4a127d0bd4", + err: nil, + }, + { + name: "Empty volume handle", + pv: &v1.PersistentVolume{ + Spec: v1.PersistentVolumeSpec{}, + }, + expected: "", + err: fmt.Errorf("got empty volume id for volume %v", &v1.PersistentVolume{}), + }, + } + + resizer := EBSVolumeResizer{} + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + volumeID, err := resizer.GetProviderVolumeID(tt.pv) + if volumeID != tt.expected || (err != nil && err.Error() != tt.err.Error()) { + t.Errorf("expected %v, got %v, expected err %v, got %v", tt.expected, volumeID, tt.err, err) + } + }) + } +} + +func TestVolumeBelongsToProvider(t *testing.T) { + tests := []struct { + name string + pv *v1.PersistentVolume + expected bool + }{ + { + name: "CSI volume handle", + pv: &v1.PersistentVolume{ + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeSource: v1.PersistentVolumeSource{ + CSI: &v1.CSIPersistentVolumeSource{ + Driver: "ebs.csi.aws.com", + VolumeHandle: "vol-075ddfc4a127d0bd5", + }, + }, + }, + }, + expected: true, + }, + { + name: "AWS EBS volume handle", + pv: &v1.PersistentVolume{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string { + "pv.kubernetes.io/provisioned-by": "kubernetes.io/aws-ebs", + }, + }, + Spec: v1.PersistentVolumeSpec{ + PersistentVolumeSource: v1.PersistentVolumeSource{ + AWSElasticBlockStore: &v1.AWSElasticBlockStoreVolumeSource{ + VolumeID: "aws://eu-central-1a/vol-075ddfc4a127d0bd4", + }, + }, + }, + }, + expected: true, + }, + { + name: "Empty volume source", + pv: &v1.PersistentVolume{ + Spec: v1.PersistentVolumeSpec{}, + }, + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + resizer := EBSVolumeResizer{} + isProvider := resizer.VolumeBelongsToProvider(tt.pv) + if isProvider != tt.expected { + t.Errorf("expected %v, got %v", tt.expected, isProvider) + } + }) + } +} From bb6242e3c9106bd2b8f4cc61021e09b62465f9d8 Mon Sep 17 00:00:00 2001 From: zyue110026 <98426905+zyue110026@users.noreply.github.com> Date: Thu, 19 Dec 2024 07:12:15 -0600 Subject: [PATCH 25/34] fix: replicaCount not being respect (#2708) Co-authored-by: Felix Kunde --- charts/postgres-operator-ui/templates/deployment.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index 3161ae0a7..899b07d97 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -9,7 +9,7 @@ metadata: name: {{ template "postgres-operator-ui.fullname" . }} namespace: {{ .Release.Namespace }} spec: - replicas: 1 + replicas: {{ .Values.replicaCount }} selector: matchLabels: app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} @@ -102,4 +102,4 @@ spec: {{ toYaml .Values.tolerations | indent 8 }} {{- if .Values.priorityClassName }} priorityClassName: {{ .Values.priorityClassName }} - {{- end }} \ No newline at end of file + {{- end }} From 34df486f00c97f5a8a73acd78103512d340ef378 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Thu, 19 Dec 2024 17:35:01 +0100 Subject: [PATCH 26/34] fix flaky comparison unit test of retruned errors (#2822) --- pkg/cluster/streams_test.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/cluster/streams_test.go b/pkg/cluster/streams_test.go index dd76a41f4..934f2bfd4 100644 --- a/pkg/cluster/streams_test.go +++ b/pkg/cluster/streams_test.go @@ -76,7 +76,6 @@ var ( }, BatchSize: k8sutil.UInt32ToPointer(uint32(100)), CPU: k8sutil.StringToPointer("250m"), - Memory: k8sutil.StringToPointer("500Mi"), }, }, TeamID: "acid", @@ -95,8 +94,7 @@ var ( Name: fmt.Sprintf("%s-12345", clusterName), Namespace: namespace, Annotations: map[string]string{ - constants.EventStreamCpuAnnotationKey: "250m", - constants.EventStreamMemoryAnnotationKey: "500Mi", + constants.EventStreamCpuAnnotationKey: "250m", }, Labels: map[string]string{ "application": "spilo", @@ -654,7 +652,7 @@ func TestSameStreams(t *testing.T) { streamsA: newFabricEventStream([]zalandov1.EventStream{stream1, stream2}, nil), streamsB: fes, match: false, - reason: "new streams annotations do not match: Added \"fes.zalando.org/FES_CPU\" with value \"250m\". Added \"fes.zalando.org/FES_MEMORY\" with value \"500Mi\"., new streams labels do not match the current ones, new streams EventStreams array does not match : number of defined streams is different", + reason: "new streams annotations do not match: Added \"fes.zalando.org/FES_CPU\" with value \"250m\"., new streams labels do not match the current ones, new streams EventStreams array does not match : number of defined streams is different", }, { subTest: "event stream recovery specs differ", From 470a1eab897f51f100a42ac2a21e1817645ff69c Mon Sep 17 00:00:00 2001 From: Ida Novindasari Date: Fri, 20 Dec 2024 11:22:52 +0100 Subject: [PATCH 27/34] Add support for pg17 and remove pg12 (#2773) * Add support for pg17 * use new gcov2lcov-action * Use ghcr spilo-17 * Update SPILO_CURRENT and SPILO_LAZY * Update e2e/run.sh --------- Co-authored-by: Polina Bungina <27892524+hughcapet@users.noreply.github.com> --- .github/workflows/run_tests.yaml | 2 +- README.md | 6 +-- .../templates/deployment.yaml | 4 +- .../crds/operatorconfigurations.yaml | 6 +-- .../postgres-operator/crds/postgresqls.yaml | 2 +- charts/postgres-operator/values.yaml | 6 +-- docs/administrator.md | 2 +- docs/reference/cluster_manifest.md | 4 +- docs/reference/operator_parameters.md | 7 +-- docs/user.md | 12 ++--- e2e/Makefile | 2 +- e2e/run.sh | 2 +- e2e/tests/test_e2e.py | 47 +++++++++---------- logical-backup/Dockerfile | 2 +- manifests/complete-postgres-manifest.yaml | 4 +- manifests/configmap.yaml | 6 +-- ...mal-postgres-lowest-version-manifest.yaml} | 2 +- manifests/minimal-postgres-manifest.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 6 +-- ...gresql-operator-default-configuration.yaml | 6 +-- manifests/postgresql.crd.yaml | 2 +- manifests/standby-manifest.yaml | 2 +- pkg/apis/acid.zalan.do/v1/crds.go | 9 ++-- .../v1/operator_configuration_type.go | 4 +- pkg/apis/acid.zalan.do/v1/util_test.go | 10 ++-- pkg/cluster/k8sres_test.go | 30 ++++++------ pkg/cluster/majorversionupgrade.go | 3 +- pkg/controller/operator_config.go | 6 +-- pkg/util/config/config.go | 6 +-- ui/manifests/deployment.yaml | 4 +- ui/operator_ui/main.py | 2 +- ui/operator_ui/spiloutils.py | 2 +- ui/run_local.sh | 4 +- 33 files changed, 106 insertions(+), 108 deletions(-) rename manifests/{minimal-postgres-manifest-12.yaml => minimal-postgres-lowest-version-manifest.yaml} (95%) diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index c0e731e5e..32bd2931d 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -22,7 +22,7 @@ jobs: - name: Run unit tests run: go test -race -covermode atomic -coverprofile=coverage.out ./... - name: Convert coverage to lcov - uses: jandelgado/gcov2lcov-action@v1.0.9 + uses: jandelgado/gcov2lcov-action@v1.1.1 - name: Coveralls uses: coverallsapp/github-action@master with: diff --git a/README.md b/README.md index c34bc6f6f..bf393d2da 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,13 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as ### PostgreSQL features -* Supports PostgreSQL 16, starting from 12+ +* Supports PostgreSQL 17, starting from 13+ * Streaming replication cluster via Patroni * Point-In-Time-Recovery with -[pg_basebackup](https://www.postgresql.org/docs/16/app-pgbasebackup.html) / +[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html) / [WAL-E](https://github.com/wal-e/wal-e) via [Spilo](https://github.com/zalando/spilo) * Preload libraries: [bg_mon](https://github.com/CyberDem0n/bg_mon), -[pg_stat_statements](https://www.postgresql.org/docs/16/pgstatstatements.html), +[pg_stat_statements](https://www.postgresql.org/docs/17/pgstatstatements.html), [pgextwlist](https://github.com/dimitri/pgextwlist), [pg_auth_mon](https://github.com/RafiaSabih/pg_auth_mon) * Incl. popular Postgres extensions such as diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index 899b07d97..fbb9ee086 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -84,11 +84,11 @@ spec: "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "17", "16", "15", "14", - "13", - "12" + "13" ] } {{- if .Values.extraEnvs }} diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 0a1e74613..058769acf 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -68,7 +68,7 @@ spec: type: string docker_image: type: string - default: "ghcr.io/zalando/spilo-16:3.3-p1" + default: "ghcr.io/zalando/spilo-17:4.0-p2" enable_crd_registration: type: boolean default: true @@ -167,10 +167,10 @@ spec: type: string minimal_major_version: type: string - default: "12" + default: "13" target_major_version: type: string - default: "16" + default: "17" kubernetes: type: object properties: diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index a83f7cc95..8083e5e1d 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -375,11 +375,11 @@ spec: version: type: string enum: - - "12" - "13" - "14" - "15" - "16" + - "17" parameters: type: object additionalProperties: diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 472be7443..881ff05d6 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -38,7 +38,7 @@ configGeneral: # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" # Spilo docker image - docker_image: ghcr.io/zalando/spilo-16:3.3-p1 + docker_image: ghcr.io/zalando/spilo-17:4.0-p2 # key name for annotation to ignore globally configured instance limits # ignore_instance_limits_annotation_key: "" @@ -89,9 +89,9 @@ configMajorVersionUpgrade: # - acid # minimal Postgres major version that will not automatically be upgraded - minimal_major_version: "12" + minimal_major_version: "13" # target Postgres major version when upgrading clusters automatically - target_major_version: "16" + target_major_version: "17" configKubernetes: # list of additional capabilities for postgres container diff --git a/docs/administrator.md b/docs/administrator.md index 725e93716..b06b4ca85 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -1297,7 +1297,7 @@ aws_or_gcp: If cluster members have to be (re)initialized restoring physical backups happens automatically either from the backup location or by running -[pg_basebackup](https://www.postgresql.org/docs/16/app-pgbasebackup.html) +[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html) on one of the other running instances (preferably replicas if they do not lag behind). You can test restoring backups by [cloning](user.md#how-to-clone-an-existing-postgresql-cluster) clusters. diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index 610982c73..8d02ee7d8 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -638,7 +638,7 @@ the global configuration before adding the `tls` section'. ## Change data capture streams This sections enables change data capture (CDC) streams via Postgres' -[logical decoding](https://www.postgresql.org/docs/16/logicaldecoding.html) +[logical decoding](https://www.postgresql.org/docs/17/logicaldecoding.html) feature and `pgoutput` plugin. While the Postgres operator takes responsibility for providing the setup to publish change events, it relies on external tools to consume them. At Zalando, we are using a workflow based on @@ -671,7 +671,7 @@ can have the following properties: The CDC operator is following the [outbox pattern](https://debezium.io/blog/2019/02/19/reliable-microservices-data-exchange-with-the-outbox-pattern/). The application is responsible for putting events into a (JSON/B or VARCHAR) payload column of the outbox table in the structure of the specified target - event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/16/logical-replication-publication.html) + event type. The operator will create a [PUBLICATION](https://www.postgresql.org/docs/17/logical-replication-publication.html) in Postgres for all tables specified for one `database` and `applicationId`. The CDC operator will consume from it shortly after transactions are committed to the outbox table. The `idColumn` will be used in telemetry for diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 4d4d16cdb..3bd9e44f7 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -94,9 +94,6 @@ Those are top-level keys, containing both leaf keys and groups. * **enable_pgversion_env_var** With newer versions of Spilo, it is preferable to use `PGVERSION` pod environment variable instead of the setting `postgresql.bin_dir` in the `SPILO_CONFIGURATION` env variable. When this option is true, the operator sets `PGVERSION` and omits `postgresql.bin_dir` from `SPILO_CONFIGURATION`. When false, the `postgresql.bin_dir` is set. This setting takes precedence over `PGVERSION`; see PR 222 in Spilo. The default is `true`. -* **enable_spilo_wal_path_compat** - enables backwards compatible path between Spilo 12 and Spilo 13+ images. The default is `false`. - * **enable_team_id_clustername_prefix** To lower the risk of name clashes between clusters of different teams you can turn on this flag and the operator will sync only clusters where the @@ -250,12 +247,12 @@ CRD-configuration, they are grouped under the `major_version_upgrade` key. * **minimal_major_version** The minimal Postgres major version that will not automatically be upgraded - when `major_version_upgrade_mode` is set to `"full"`. The default is `"12"`. + when `major_version_upgrade_mode` is set to `"full"`. The default is `"13"`. * **target_major_version** The target Postgres major version when upgrading clusters automatically which violate the configured allowed `minimal_major_version` when - `major_version_upgrade_mode` is set to `"full"`. The default is `"16"`. + `major_version_upgrade_mode` is set to `"full"`. The default is `"17"`. ## Kubernetes resources diff --git a/docs/user.md b/docs/user.md index 78b30dfe9..aba65c11d 100644 --- a/docs/user.md +++ b/docs/user.md @@ -30,7 +30,7 @@ spec: databases: foo: zalando postgresql: - version: "16" + version: "17" ``` Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator) @@ -109,7 +109,7 @@ metadata: spec: [...] postgresql: - version: "16" + version: "17" parameters: password_encryption: scram-sha-256 ``` @@ -517,7 +517,7 @@ Postgres Operator will create the following NOLOGIN roles: The `_owner` role is the database owner and should be used when creating new database objects. All members of the `admin` role, e.g. teams API roles, can -become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/16/sql-alterdefaultprivileges.html) +become the owner with the `SET ROLE` command. [Default privileges](https://www.postgresql.org/docs/17/sql-alterdefaultprivileges.html) are configured for the owner role so that the `_reader` role automatically gets read-access (SELECT) to new tables and sequences and the `_writer` receives write-access (INSERT, UPDATE, DELETE on tables, @@ -594,7 +594,7 @@ spec: ### Schema `search_path` for default roles -The schema [`search_path`](https://www.postgresql.org/docs/16/ddl-schemas.html#DDL-SCHEMAS-PATH) +The schema [`search_path`](https://www.postgresql.org/docs/17/ddl-schemas.html#DDL-SCHEMAS-PATH) for each role will include the role name and the schemas, this role should have access to. So `foo_bar_writer` does not have to schema-qualify tables from schemas `foo_bar_writer, bar`, while `foo_writer` can look up `foo_writer` and @@ -695,7 +695,7 @@ handle it. ### HugePages support -The operator supports [HugePages](https://www.postgresql.org/docs/16/kernel-resources.html#LINUX-HUGEPAGES). +The operator supports [HugePages](https://www.postgresql.org/docs/17/kernel-resources.html#LINUX-HUGEPAGES). To enable HugePages, set the matching resource requests and/or limits in the manifest: ```yaml @@ -838,7 +838,7 @@ spec: ### Clone directly Another way to get a fresh copy of your source DB cluster is via -[pg_basebackup](https://www.postgresql.org/docs/16/app-pgbasebackup.html). To +[pg_basebackup](https://www.postgresql.org/docs/17/app-pgbasebackup.html). To use this feature simply leave out the timestamp field from the clone section. The operator will connect to the service of the source cluster by name. If the cluster is called test, then the connection string will look like host=test diff --git a/e2e/Makefile b/e2e/Makefile index 8e200dab1..52d24e9e5 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -46,7 +46,7 @@ tools: # install pinned version of 'kind' # go install must run outside of a dir with a (module-based) Go project ! # otherwise go install updates project's dependencies and/or behaves differently - cd "/tmp" && GO111MODULE=on go install sigs.k8s.io/kind@v0.23.0 + cd "/tmp" && GO111MODULE=on go install sigs.k8s.io/kind@v0.24.0 e2etest: tools copy clean ./run.sh main diff --git a/e2e/run.sh b/e2e/run.sh index 1adca479d..d289cb3f4 100755 --- a/e2e/run.sh +++ b/e2e/run.sh @@ -8,7 +8,7 @@ IFS=$'\n\t' readonly cluster_name="postgres-operator-e2e-tests" readonly kubeconfig_path="/tmp/kind-config-${cluster_name}" -readonly spilo_image="registry.opensource.zalan.do/acid/spilo-16-e2e:0.1" +readonly spilo_image="registry.opensource.zalan.do/acid/spilo-17-e2e:0.3" readonly e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.4" export GOPATH=${GOPATH-~/go} diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index f5a05a157..04c6465c9 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -12,10 +12,9 @@ from kubernetes import client from tests.k8s_api import K8s from kubernetes.client.rest import ApiException -SPILO_CURRENT = "registry.opensource.zalan.do/acid/spilo-16-e2e:0.1" -SPILO_LAZY = "registry.opensource.zalan.do/acid/spilo-16-e2e:0.2" -SPILO_FULL_IMAGE = "ghcr.io/zalando/spilo-16:3.2-p3" - +SPILO_CURRENT = "registry.opensource.zalan.do/acid/spilo-17-e2e:0.3" +SPILO_LAZY = "registry.opensource.zalan.do/acid/spilo-17-e2e:0.4" +SPILO_FULL_IMAGE = "ghcr.io/zalando/spilo-17:4.0-p2" def to_selector(labels): return ",".join(["=".join(lbl) for lbl in labels.items()]) @@ -1201,35 +1200,35 @@ class EndToEndTestCase(unittest.TestCase): k8s = self.k8s cluster_label = 'application=spilo,cluster-name=acid-upgrade-test' - with open("manifests/minimal-postgres-manifest-12.yaml", 'r+') as f: + with open("manifests/minimal-postgres-lowest-version-manifest.yaml", 'r+') as f: upgrade_manifest = yaml.safe_load(f) upgrade_manifest["spec"]["dockerImage"] = SPILO_FULL_IMAGE - with open("manifests/minimal-postgres-manifest-12.yaml", 'w') as f: + with open("manifests/minimal-postgres-lowest-version-manifest.yaml", 'w') as f: yaml.dump(upgrade_manifest, f, Dumper=yaml.Dumper) - k8s.create_with_kubectl("manifests/minimal-postgres-manifest-12.yaml") + k8s.create_with_kubectl("manifests/minimal-postgres-lowest-version-manifest.yaml") self.eventuallyEqual(lambda: k8s.count_running_pods(labels=cluster_label), 2, "No 2 pods running") self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") - self.eventuallyEqual(check_version, 12, "Version is not correct") + self.eventuallyEqual(check_version, 13, "Version is not correct") master_nodes, _ = k8s.get_cluster_nodes(cluster_labels=cluster_label) # should upgrade immediately - pg_patch_version_13 = { + pg_patch_version_14 = { "spec": { "postgresql": { - "version": "13" + "version": "14" } } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_13) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_14) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") 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) - self.eventuallyEqual(check_version, 13, "Version should be upgraded from 12 to 13") + self.eventuallyEqual(check_version, 14, "Version should be upgraded from 13 to 14") # check if annotation for last upgrade's success is set annotations = get_annotations() @@ -1238,10 +1237,10 @@ class EndToEndTestCase(unittest.TestCase): # should not upgrade because current time is not in maintenanceWindow current_time = datetime.now() maintenance_window_future = f"{(current_time+timedelta(minutes=60)).strftime('%H:%M')}-{(current_time+timedelta(minutes=120)).strftime('%H:%M')}" - pg_patch_version_14 = { + pg_patch_version_15 = { "spec": { "postgresql": { - "version": "14" + "version": "15" }, "maintenanceWindows": [ maintenance_window_future @@ -1249,23 +1248,23 @@ class EndToEndTestCase(unittest.TestCase): } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_14) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") k8s.wait_for_pod_failover(master_nodes, 'spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 13, "Version should not be upgraded") + self.eventuallyEqual(check_version, 14, "Version should not be upgraded") second_annotations = get_annotations() self.assertIsNone(second_annotations.get("last-major-upgrade-failure"), "Annotation for last upgrade's failure should not be set") # change the version again to trigger operator sync maintenance_window_current = f"{(current_time-timedelta(minutes=30)).strftime('%H:%M')}-{(current_time+timedelta(minutes=30)).strftime('%H:%M')}" - pg_patch_version_15 = { + pg_patch_version_16 = { "spec": { "postgresql": { - "version": "15" + "version": "16" }, "maintenanceWindows": [ maintenance_window_current @@ -1274,13 +1273,13 @@ class EndToEndTestCase(unittest.TestCase): } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_15) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_16) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") 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) - self.eventuallyEqual(check_version, 15, "Version should be upgraded from 13 to 15") + self.eventuallyEqual(check_version, 16, "Version should be upgraded from 14 to 16") # check if annotation for last upgrade's success is updated after second upgrade third_annotations = get_annotations() @@ -1288,7 +1287,7 @@ class EndToEndTestCase(unittest.TestCase): self.assertNotEqual(annotations.get("last-major-upgrade-success"), third_annotations.get("last-major-upgrade-success"), "Annotation for last upgrade's success is not updated") # test upgrade with failed upgrade annotation - pg_patch_version_16 = { + pg_patch_version_17 = { "metadata": { "annotations": { "last-major-upgrade-failure": "2024-01-02T15:04:05Z" @@ -1296,18 +1295,18 @@ class EndToEndTestCase(unittest.TestCase): }, "spec": { "postgresql": { - "version": "16" + "version": "17" }, }, } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_16) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-upgrade-test", pg_patch_version_17) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") k8s.wait_for_pod_failover(master_nodes, 'spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=master,' + cluster_label) k8s.wait_for_pod_start('spilo-role=replica,' + cluster_label) - self.eventuallyEqual(check_version, 15, "Version should not be upgraded because annotation for last upgrade's failure is set") + self.eventuallyEqual(check_version, 16, "Version should not be upgraded because annotation for last upgrade's failure is set") # change the version back to 15 and should remove failure annotation k8s.api.custom_objects_api.patch_namespaced_custom_object( diff --git a/logical-backup/Dockerfile b/logical-backup/Dockerfile index 8770e5e1a..137f4efa8 100644 --- a/logical-backup/Dockerfile +++ b/logical-backup/Dockerfile @@ -25,11 +25,11 @@ RUN apt-get update \ && curl --silent https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ && apt-get update \ && apt-get install --no-install-recommends -y \ + postgresql-client-17 \ postgresql-client-16 \ postgresql-client-15 \ postgresql-client-14 \ postgresql-client-13 \ - postgresql-client-12 \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index 0b3dc4aa7..44d317123 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -10,7 +10,7 @@ metadata: # "delete-date": "2020-08-31" # can only be deleted on that day if "delete-date "key is configured # "delete-clustername": "acid-test-cluster" # can only be deleted when name matches if "delete-clustername" key is configured spec: - dockerImage: ghcr.io/zalando/spilo-16:3.3-p1 + dockerImage: ghcr.io/zalando/spilo-17:4.0-p2 teamId: "acid" numberOfInstances: 2 users: # Application/Robot users @@ -48,7 +48,7 @@ spec: defaultRoles: true defaultUsers: false postgresql: - version: "16" + version: "17" parameters: # Expert section shared_buffers: "32MB" max_connections: "10" diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 1c8c8fdfd..094bd6bd5 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -34,7 +34,7 @@ data: default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername - docker_image: ghcr.io/zalando/spilo-16:3.3-p1 + docker_image: ghcr.io/zalando/spilo-17:4.0-p2 # downscaler_annotations: "deployment-time,downscaler/*" enable_admin_role_for_users: "true" enable_crd_registration: "true" @@ -112,7 +112,7 @@ data: min_cpu_limit: 250m min_instances: "-1" min_memory_limit: 250Mi - minimal_major_version: "12" + minimal_major_version: "13" # node_readiness_label: "status:ready" # node_readiness_label_merge: "OR" oauth_token_secret_name: postgresql-operator @@ -162,7 +162,7 @@ data: spilo_privileged: "false" storage_resize_mode: "pvc" super_username: postgres - target_major_version: "16" + target_major_version: "17" team_admin_role: "admin" team_api_role_configuration: "log_statement:all" teams_api_url: http://fake-teams-api.default.svc.cluster.local diff --git a/manifests/minimal-postgres-manifest-12.yaml b/manifests/minimal-postgres-lowest-version-manifest.yaml similarity index 95% rename from manifests/minimal-postgres-manifest-12.yaml rename to manifests/minimal-postgres-lowest-version-manifest.yaml index d578ac46d..40abf0c9c 100644 --- a/manifests/minimal-postgres-manifest-12.yaml +++ b/manifests/minimal-postgres-lowest-version-manifest.yaml @@ -17,4 +17,4 @@ spec: preparedDatabases: bar: {} postgresql: - version: "12" + version: "13" diff --git a/manifests/minimal-postgres-manifest.yaml b/manifests/minimal-postgres-manifest.yaml index d22327905..8b1ed275d 100644 --- a/manifests/minimal-postgres-manifest.yaml +++ b/manifests/minimal-postgres-manifest.yaml @@ -17,4 +17,4 @@ spec: preparedDatabases: bar: {} postgresql: - version: "16" + version: "17" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index a7b1a7280..d4990bf2b 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -66,7 +66,7 @@ spec: type: string docker_image: type: string - default: "ghcr.io/zalando/spilo-16:3.3-p1" + default: "ghcr.io/zalando/spilo-17:4.0-p2" enable_crd_registration: type: boolean default: true @@ -165,10 +165,10 @@ spec: type: string minimal_major_version: type: string - default: "12" + default: "13" target_major_version: type: string - default: "16" + default: "17" kubernetes: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index ecb7a03de..db0d13b5f 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -3,7 +3,7 @@ kind: OperatorConfiguration metadata: name: postgresql-operator-default-configuration configuration: - docker_image: ghcr.io/zalando/spilo-16:3.3-p1 + docker_image: ghcr.io/zalando/spilo-17:4.0-p2 # enable_crd_registration: true # crd_categories: # - all @@ -39,8 +39,8 @@ configuration: major_version_upgrade_mode: "manual" # major_version_upgrade_team_allow_list: # - acid - minimal_major_version: "12" - target_major_version: "16" + minimal_major_version: "13" + target_major_version: "17" kubernetes: # additional_pod_capabilities: # - "SYS_NICE" diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 9f7e3eff8..39d751cef 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -373,11 +373,11 @@ spec: version: type: string enum: - - "12" - "13" - "14" - "15" - "16" + - "17" parameters: type: object additionalProperties: diff --git a/manifests/standby-manifest.yaml b/manifests/standby-manifest.yaml index aece29dae..eb90464a6 100644 --- a/manifests/standby-manifest.yaml +++ b/manifests/standby-manifest.yaml @@ -8,7 +8,7 @@ spec: size: 1Gi numberOfInstances: 1 postgresql: - version: "16" + version: "17" # Make this a standby cluster and provide either the s3 bucket path of source cluster or the remote primary host for continuous streaming. standby: # s3_wal_path: "s3://mybucket/spilo/acid-minimal-cluster/abcd1234-2a4b-4b2a-8c9c-c1234defg567/wal/14/" diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index c5c4b2706..3f6bf25d9 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -595,9 +595,6 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ "version": { Type: "string", Enum: []apiextv1.JSON{ - { - Raw: []byte(`"12"`), - }, { Raw: []byte(`"13"`), }, @@ -610,6 +607,9 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ { Raw: []byte(`"16"`), }, + { + Raw: []byte(`"17"`), + }, }, }, "parameters": { @@ -1164,7 +1164,8 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ Type: "boolean", }, "enable_spilo_wal_path_compat": { - Type: "boolean", + Type: "boolean", + Description: "deprecated", }, "enable_team_id_clustername_prefix": { Type: "boolean", diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index eb01d450c..cd11b9173 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -49,8 +49,8 @@ type PostgresUsersConfiguration struct { type MajorVersionUpgradeConfiguration struct { MajorVersionUpgradeMode string `json:"major_version_upgrade_mode" default:"manual"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade MajorVersionUpgradeTeamAllowList []string `json:"major_version_upgrade_team_allow_list,omitempty"` - MinimalMajorVersion string `json:"minimal_major_version" default:"12"` - TargetMajorVersion string `json:"target_major_version" default:"16"` + MinimalMajorVersion string `json:"minimal_major_version" default:"13"` + TargetMajorVersion string `json:"target_major_version" default:"17"` } // KubernetesMetaConfiguration defines k8s conf required for all Postgres clusters and the operator itself diff --git a/pkg/apis/acid.zalan.do/v1/util_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go index bef6cc3ec..5e4913ffe 100644 --- a/pkg/apis/acid.zalan.do/v1/util_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -219,7 +219,7 @@ var unmarshalCluster = []struct { "127.0.0.1/32" ], "postgresql": { - "version": "16", + "version": "17", "parameters": { "shared_buffers": "32MB", "max_connections": "10", @@ -279,7 +279,7 @@ var unmarshalCluster = []struct { }, Spec: PostgresSpec{ PostgresqlParam: PostgresqlParam{ - PgVersion: "16", + PgVersion: "17", Parameters: map[string]string{ "shared_buffers": "32MB", "max_connections": "10", @@ -339,7 +339,7 @@ var unmarshalCluster = []struct { }, Error: "", }, - marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"16","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), + marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"17","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`), err: nil}, { about: "example with clone", @@ -404,7 +404,7 @@ var postgresqlList = []struct { out PostgresqlList err error }{ - {"expect success", []byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"16"},"teamId":"acid","volume":{"size":"10Gi"}},"status":{"PostgresClusterStatus":"Running"}}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), + {"expect success", []byte(`{"apiVersion":"v1","items":[{"apiVersion":"acid.zalan.do/v1","kind":"Postgresql","metadata":{"labels":{"team":"acid"},"name":"acid-testcluster42","namespace":"default","resourceVersion":"30446957","selfLink":"/apis/acid.zalan.do/v1/namespaces/default/postgresqls/acid-testcluster42","uid":"857cd208-33dc-11e7-b20a-0699041e4b03"},"spec":{"allowedSourceRanges":["185.85.220.0/22"],"numberOfInstances":1,"postgresql":{"version":"17"},"teamId":"acid","volume":{"size":"10Gi"}},"status":{"PostgresClusterStatus":"Running"}}],"kind":"List","metadata":{},"resourceVersion":"","selfLink":""}`), PostgresqlList{ TypeMeta: metav1.TypeMeta{ Kind: "List", @@ -425,7 +425,7 @@ var postgresqlList = []struct { }, Spec: PostgresSpec{ ClusterName: "testcluster42", - PostgresqlParam: PostgresqlParam{PgVersion: "16"}, + PostgresqlParam: PostgresqlParam{PgVersion: "17"}, Volume: Volume{Size: "10Gi"}, TeamID: "acid", AllowedSourceRanges: []string{"185.85.220.0/22"}, diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index bea229dda..612e4525a 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -72,18 +72,18 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }{ { subtest: "Patroni default configuration", - pgParam: &acidv1.PostgresqlParam{PgVersion: "16"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, patroni: &acidv1.Patroni{}, opConfig: &config.Config{ Auth: config.Auth{ PamRoleName: "zalandos", }, }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/16/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{}}}`, }, { subtest: "Patroni configured", - pgParam: &acidv1.PostgresqlParam{PgVersion: "16"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, patroni: &acidv1.Patroni{ InitDB: map[string]string{ "encoding": "UTF8", @@ -102,38 +102,38 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { FailsafeMode: util.True(), }, opConfig: &config.Config{}, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/16/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin","pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"]},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally", - pgParam: &acidv1.PostgresqlParam{PgVersion: "16"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, patroni: &acidv1.Patroni{}, opConfig: &config.Config{ EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/16/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally, disabled for cluster", - pgParam: &acidv1.PostgresqlParam{PgVersion: "16"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, patroni: &acidv1.Patroni{ FailsafeMode: util.False(), }, opConfig: &config.Config{ EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/16/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":false}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":false}}}`, }, { subtest: "Patroni failsafe_mode disabled globally, configured for cluster", - pgParam: &acidv1.PostgresqlParam{PgVersion: "16"}, + pgParam: &acidv1.PostgresqlParam{PgVersion: "17"}, patroni: &acidv1.Patroni{ FailsafeMode: util.True(), }, opConfig: &config.Config{ EnablePatroniFailsafeMode: util.False(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/16/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/17/bin"},"bootstrap":{"initdb":[{"auth-host":"md5"},{"auth-local":"trust"}],"dcs":{"failsafe_mode":true}}}`, }, } for _, tt := range tests { @@ -164,15 +164,15 @@ func TestExtractPgVersionFromBinPath(t *testing.T) { }, { subTest: "test current bin path against hard coded template", - binPath: "/usr/lib/postgresql/16/bin", + binPath: "/usr/lib/postgresql/17/bin", template: pgBinariesLocationTemplate, - expected: "16", + expected: "17", }, { subTest: "test alternative bin path against a matching template", - binPath: "/usr/pgsql-16/bin", + binPath: "/usr/pgsql-17/bin", template: "/usr/pgsql-%v/bin", - expected: "16", + expected: "17", }, } @@ -2148,7 +2148,7 @@ func TestSidecars(t *testing.T) { spec = acidv1.PostgresSpec{ PostgresqlParam: acidv1.PostgresqlParam{ - PgVersion: "16", + PgVersion: "17", Parameters: map[string]string{ "max_connections": "100", }, diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 560f8977f..a4ae5f81b 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -21,6 +21,7 @@ var VersionMap = map[string]int{ "14": 140000, "15": 150000, "16": 160000, + "17": 170000, } const ( @@ -44,7 +45,7 @@ func (c *Cluster) GetDesiredMajorVersionAsInt() int { func (c *Cluster) GetDesiredMajorVersion() string { if c.Config.OpConfig.MajorVersionUpgradeMode == "full" { - // e.g. current is 12, minimal is 12 allowing 12 to 16 clusters, everything below is upgraded + // e.g. current is 13, minimal is 13 allowing 13 to 17 clusters, everything below is upgraded if IsBiggerPostgresVersion(c.Spec.PgVersion, c.Config.OpConfig.MinimalMajorVersion) { c.logger.Infof("overwriting configured major version %s to %s", c.Spec.PgVersion, c.Config.OpConfig.TargetMajorVersion) return c.Config.OpConfig.TargetMajorVersion diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 78e752f1d..ba347b2fd 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -39,7 +39,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableTeamIdClusternamePrefix = fromCRD.EnableTeamIdClusternamePrefix result.EtcdHost = fromCRD.EtcdHost result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps - result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-16:3.3-p1") + result.DockerImage = util.Coalesce(fromCRD.DockerImage, "ghcr.io/zalando/spilo-17:4.0-p2") result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances @@ -62,8 +62,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // major version upgrade config result.MajorVersionUpgradeMode = util.Coalesce(fromCRD.MajorVersionUpgrade.MajorVersionUpgradeMode, "manual") result.MajorVersionUpgradeTeamAllowList = fromCRD.MajorVersionUpgrade.MajorVersionUpgradeTeamAllowList - result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "12") - result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "16") + result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "13") + result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "17") // kubernetes config result.EnableOwnerReferences = util.CoalesceBool(fromCRD.Kubernetes.EnableOwnerReferences, util.False()) diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 4c7b8db10..6c76718b7 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -175,7 +175,7 @@ type Config struct { WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"` EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS - DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-16:3.3-p1"` + DockerImage string `name:"docker_image" default:"ghcr.io/zalando/spilo-17:4.0-p2"` SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"` @@ -246,8 +246,8 @@ type Config struct { EnableTeamIdClusternamePrefix bool `name:"enable_team_id_clustername_prefix" default:"false"` MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"manual"` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` - MinimalMajorVersion string `name:"minimal_major_version" default:"12"` - TargetMajorVersion string `name:"target_major_version" default:"16"` + MinimalMajorVersion string `name:"minimal_major_version" default:"13"` + TargetMajorVersion string `name:"target_major_version" default:"17"` PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index 76d2143cb..9b0038579 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -73,11 +73,11 @@ spec: "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "17", "16", "15", "14", - "13", - "12" + "13" ] } # Exemple of settings to make snapshot view working in the ui when using AWS diff --git a/ui/operator_ui/main.py b/ui/operator_ui/main.py index ba544750f..e02c2995c 100644 --- a/ui/operator_ui/main.py +++ b/ui/operator_ui/main.py @@ -267,7 +267,7 @@ DEFAULT_UI_CONFIG = { 'users_visible': True, 'databases_visible': True, 'resources_visible': RESOURCES_VISIBLE, - 'postgresql_versions': ['12', '13', '14', '15', '16'], + 'postgresql_versions': ['13', '14', '15', '16', '17'], 'dns_format_string': '{0}.{1}', 'pgui_link': '', 'static_network_whitelist': {}, diff --git a/ui/operator_ui/spiloutils.py b/ui/operator_ui/spiloutils.py index 9de072fca..f715430a1 100644 --- a/ui/operator_ui/spiloutils.py +++ b/ui/operator_ui/spiloutils.py @@ -305,7 +305,7 @@ def read_versions( if uid == 'wal' or defaulting(lambda: UUID(uid)) ] -BACKUP_VERSION_PREFIXES = ['', '10/', '11/', '12/', '13/', '14/', '15/', '16/'] +BACKUP_VERSION_PREFIXES = ['', '10/', '11/', '12/', '13/', '14/', '15/', '16/', '17/'] def read_basebackups( pg_cluster, diff --git a/ui/run_local.sh b/ui/run_local.sh index 77f4da760..37f8b1747 100755 --- a/ui/run_local.sh +++ b/ui/run_local.sh @@ -31,11 +31,11 @@ default_operator_ui_config='{ "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "17", "16", "15", "14", - "13", - "12" + "13" ], "static_network_whitelist": { "localhost": ["172.0.0.1/32"] From d97c271b84364a062d9ad611a18f0e478d59780c Mon Sep 17 00:00:00 2001 From: Demch1k Date: Mon, 23 Dec 2024 12:53:27 +0500 Subject: [PATCH 28/34] Add abitility to set QPS and Burst limits for api client (#2667) * Add abitility to set QPS and Burst limits for api client --------- Co-authored-by: Ivan Sokoryan Co-authored-by: Felix Kunde --- cmd/main.go | 5 +++++ pkg/spec/types.go | 3 +++ 2 files changed, 8 insertions(+) diff --git a/cmd/main.go b/cmd/main.go index 0b48ac863..adbf0cce5 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -35,6 +35,8 @@ func init() { flag.BoolVar(&outOfCluster, "outofcluster", false, "Whether the operator runs in- our outside of the Kubernetes cluster.") flag.BoolVar(&config.NoDatabaseAccess, "nodatabaseaccess", false, "Disable all access to the database from the operator side.") flag.BoolVar(&config.NoTeamsAPI, "noteamsapi", false, "Disable all access to the teams API") + flag.IntVar(&config.KubeQPS, "kubeqps", 10, "Kubernetes api requests per second.") + flag.IntVar(&config.KubeBurst, "kubeburst", 20, "Kubernetes api requests burst limit.") flag.Parse() config.EnableJsonLogging = os.Getenv("ENABLE_JSON_LOGGING") == "true" @@ -83,6 +85,9 @@ func main() { log.Fatalf("couldn't get REST config: %v", err) } + config.RestConfig.QPS = float32(config.KubeQPS) + config.RestConfig.Burst = config.KubeBurst + c := controller.NewController(&config, "") c.Run(stop, wg) diff --git a/pkg/spec/types.go b/pkg/spec/types.go index cfa293e14..d727aee42 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -122,6 +122,9 @@ type ControllerConfig struct { IgnoredAnnotations []string EnableJsonLogging bool + + KubeQPS int + KubeBurst int } // cached value for the GetOperatorNamespace From 548e387745ea67a41b4c798600b52138e2675482 Mon Sep 17 00:00:00 2001 From: Christoffer Anselm Date: Mon, 23 Dec 2024 08:59:54 +0100 Subject: [PATCH 29/34] Fix deployment extraEnvs indentation in operator chart (#2814) * Fix operator extraEnvs indentation Fix bad operator extraEnvs indentation by matching the statement to how other lists are expanded in the deployment template * Replace nindent by indent to fully mirror the other similar lines in the file --------- Co-authored-by: Felix Kunde --- charts/postgres-operator/templates/deployment.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/postgres-operator/templates/deployment.yaml b/charts/postgres-operator/templates/deployment.yaml index abd66cfc8..395843942 100644 --- a/charts/postgres-operator/templates/deployment.yaml +++ b/charts/postgres-operator/templates/deployment.yaml @@ -54,7 +54,7 @@ spec: value: {{ template "postgres-operator.controllerID" . }} {{- end }} {{- if .Values.extraEnvs }} - {{- .Values.extraEnvs | toYaml | nindent 8 }} +{{ toYaml .Values.extraEnvs | indent 8 }} {{- end }} resources: {{ toYaml .Values.resources | indent 10 }} From b276cd2f94c8b667b47143181fe2ce7eda130b78 Mon Sep 17 00:00:00 2001 From: Tabby Date: Mon, 23 Dec 2024 09:08:35 +0100 Subject: [PATCH 30/34] Feat: Support Running Sidecar with a Command. (#2449) * Feat: Support Running Sidecard with a Command. This PR addresses issue #2448 . Some containers may not have entry points, if this is the case they would need to be run using a command. This change extends the definition of sidecar so that there is an optional command field. If the field is present then the container will be run using that command. This is a two line change that is fully backward compatible. --- pkg/apis/acid.zalan.do/v1/postgresql_type.go | 1 + pkg/cluster/k8sres.go | 1 + 2 files changed, 2 insertions(+) diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 1a8a311f5..ef6dfe7ff 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -220,6 +220,7 @@ type Sidecar struct { DockerImage string `json:"image,omitempty"` Ports []v1.ContainerPort `json:"ports,omitempty"` Env []v1.EnvVar `json:"env,omitempty"` + Command []string `json:"command,omitempty"` } // UserFlags defines flags (such as superuser, nologin) that could be assigned to individual users diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 4e67dbd94..ff5536303 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1222,6 +1222,7 @@ func getSidecarContainer(sidecar acidv1.Sidecar, index int, resources *v1.Resour Resources: *resources, Env: sidecar.Env, Ports: sidecar.Ports, + Command: sidecar.Command, } } From 9b103e764e769ec8af7913219e86773c6354d173 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 23 Dec 2024 09:54:51 +0100 Subject: [PATCH 31/34] bump to go 1.23.4 (#2824) --- .github/workflows/publish_ghcr_image.yaml | 2 +- .github/workflows/run_e2e.yaml | 2 +- .github/workflows/run_tests.yaml | 2 +- Makefile | 4 ++-- README.md | 2 +- docker/DebugDockerfile | 2 +- docker/Dockerfile | 2 +- docker/build_operator.sh | 2 +- docs/developer.md | 2 +- go.mod | 2 +- kubectl-pg/go.mod | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/publish_ghcr_image.yaml b/.github/workflows/publish_ghcr_image.yaml index 7633ccc3c..d56ff2f17 100644 --- a/.github/workflows/publish_ghcr_image.yaml +++ b/.github/workflows/publish_ghcr_image.yaml @@ -23,7 +23,7 @@ jobs: - uses: actions/setup-go@v2 with: - go-version: "^1.22.5" + go-version: "^1.23.4" - name: Run unit tests run: make deps mocks test diff --git a/.github/workflows/run_e2e.yaml b/.github/workflows/run_e2e.yaml index df83a31c4..16573046e 100644 --- a/.github/workflows/run_e2e.yaml +++ b/.github/workflows/run_e2e.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v1 - uses: actions/setup-go@v2 with: - go-version: "^1.22.5" + go-version: "^1.23.4" - name: Make dependencies run: make deps mocks - name: Code generation diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml index 32bd2931d..db47f6e40 100644 --- a/.github/workflows/run_tests.yaml +++ b/.github/workflows/run_tests.yaml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-go@v2 with: - go-version: "^1.22.5" + go-version: "^1.23.4" - name: Make dependencies run: make deps mocks - name: Compile diff --git a/Makefile b/Makefile index 5944b6b8f..8fc4b36f6 100644 --- a/Makefile +++ b/Makefile @@ -69,7 +69,7 @@ docker: ${DOCKERDIR}/${DOCKERFILE} docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)$(DEBUG_FRESH)$(DEBUG_POSTFIX)" -f "${DOCKERDIR}/${DOCKERFILE}" --build-arg VERSION="${VERSION}" . indocker-race: - docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.22.5 bash -c "make linux" + docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.23.4 bash -c "make linux" push: docker push "$(IMAGE):$(TAG)$(CDP_TAG)" @@ -78,7 +78,7 @@ mocks: GO111MODULE=on go generate ./... tools: - GO111MODULE=on go get -d k8s.io/client-go@kubernetes-1.30.4 + GO111MODULE=on go get k8s.io/client-go@kubernetes-1.30.4 GO111MODULE=on go install github.com/golang/mock/mockgen@v1.6.0 GO111MODULE=on go mod tidy diff --git a/README.md b/README.md index bf393d2da..9493115de 100644 --- a/README.md +++ b/README.md @@ -57,12 +57,12 @@ production for over five years. | Release | Postgres versions | K8s versions | Golang | | :-------- | :---------------: | :---------------: | :-----: | +| v1.14.0 | 13 → 17 | 1.27+ | 1.23.4 | | v1.13.0 | 12 → 16 | 1.27+ | 1.22.5 | | v1.12.0 | 11 → 16 | 1.27+ | 1.22.3 | | v1.11.0 | 11 → 16 | 1.27+ | 1.21.7 | | v1.10.1 | 10 → 15 | 1.21+ | 1.19.8 | | v1.9.0 | 10 → 15 | 1.21+ | 1.18.9 | -| v1.8.2 | 9.5 → 14 | 1.20 → 1.24 | 1.17.4 | ## Getting started diff --git a/docker/DebugDockerfile b/docker/DebugDockerfile index ec1ff6d2f..18cb631fe 100644 --- a/docker/DebugDockerfile +++ b/docker/DebugDockerfile @@ -1,4 +1,4 @@ -FROM golang:1.22-alpine +FROM golang:1.23-alpine LABEL maintainer="Team ACID @ Zalando " # We need root certificates to deal with teams api over https diff --git a/docker/Dockerfile b/docker/Dockerfile index b0808c3bc..1fd2020d8 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ ARG BASE_IMAGE=registry.opensource.zalan.do/library/alpine-3:latest -FROM golang:1.22-alpine AS builder +FROM golang:1.23-alpine AS builder ARG VERSION=latest COPY . /go/src/github.com/zalando/postgres-operator diff --git a/docker/build_operator.sh b/docker/build_operator.sh index 2ada63a81..6c1817b1b 100644 --- a/docker/build_operator.sh +++ b/docker/build_operator.sh @@ -13,7 +13,7 @@ apt-get install -y wget ( cd /tmp - wget -q "https://storage.googleapis.com/golang/go1.22.5.linux-${arch}.tar.gz" -O go.tar.gz + wget -q "https://storage.googleapis.com/golang/go1.23.4.linux-${arch}.tar.gz" -O go.tar.gz tar -xf go.tar.gz mv go /usr/local ln -s /usr/local/go/bin/go /usr/bin/go diff --git a/docs/developer.md b/docs/developer.md index 31f48d92d..c006aded0 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -186,7 +186,7 @@ go get -u github.com/derekparker/delve/cmd/dlv ``` RUN apk --no-cache add go git musl-dev -RUN go get -d github.com/derekparker/delve/cmd/dlv +RUN go get github.com/derekparker/delve/cmd/dlv ``` * Update the `Makefile` to build the project with debugging symbols. For that diff --git a/go.mod b/go.mod index 760cd3fbf..9c0125229 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/zalando/postgres-operator -go 1.22.0 +go 1.23.4 require ( github.com/aws/aws-sdk-go v1.53.8 diff --git a/kubectl-pg/go.mod b/kubectl-pg/go.mod index 036a48bdc..9b2e1bbc5 100644 --- a/kubectl-pg/go.mod +++ b/kubectl-pg/go.mod @@ -1,6 +1,6 @@ module github.com/zalando/postgres-operator/kubectl-pg -go 1.22.0 +go 1.23.4 require ( github.com/spf13/cobra v1.8.1 From 265f2a0f1c8a5c71efd8aa877112bca7b6a1449f Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 23 Dec 2024 09:58:48 +0100 Subject: [PATCH 32/34] add sidecar command examples and update codegen (#2825) --- docs/administrator.md | 4 ++++ docs/user.md | 1 + pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go | 5 +++++ 3 files changed, 10 insertions(+) diff --git a/docs/administrator.md b/docs/administrator.md index b06b4ca85..55abebc8b 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -1405,6 +1405,10 @@ configuration: volumeMounts: - mountPath: /custom-pgdata-mountpoint name: pgdata + env: + - name: "ENV_VAR_NAME" + value: "any-k8s-env-things" + command: ['sh', '-c', 'echo "logging" > /opt/logs.txt'] - ... ``` diff --git a/docs/user.md b/docs/user.md index aba65c11d..c63e43f57 100644 --- a/docs/user.md +++ b/docs/user.md @@ -1005,6 +1005,7 @@ spec: env: - name: "ENV_VAR_NAME" value: "any-k8s-env-things" + command: ['sh', '-c', 'echo "logging" > /opt/logs.txt'] ``` In addition to any environment variables you specify, the following environment diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 7c0b3ee23..ec2d359c8 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -1277,6 +1277,11 @@ func (in *Sidecar) DeepCopyInto(out *Sidecar) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Command != nil { + in, out := &in.Command, &out.Command + *out = make([]string, len(*in)) + copy(*out, *in) + } return } From df3f68bcfbf350f580922fffdc494735a0064fc9 Mon Sep 17 00:00:00 2001 From: Mario Trangoni Date: Mon, 23 Dec 2024 11:10:44 +0100 Subject: [PATCH 33/34] manifests/minimal-master-replica-svcmonitor.yaml: Update postgres-exporter image (#2777) Signed-off-by: Mario Trangoni Co-authored-by: Felix Kunde --- manifests/minimal-master-replica-svcmonitor.yaml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/manifests/minimal-master-replica-svcmonitor.yaml b/manifests/minimal-master-replica-svcmonitor.yaml index 67ed28c81..049ea12eb 100644 --- a/manifests/minimal-master-replica-svcmonitor.yaml +++ b/manifests/minimal-master-replica-svcmonitor.yaml @@ -31,11 +31,21 @@ spec: version: "13" sidecars: - name: "exporter" - image: "wrouesnel/postgres_exporter" + image: "quay.io/prometheuscommunity/postgres-exporter:v0.15.0" ports: - name: exporter containerPort: 9187 protocol: TCP + env: + - name: DATA_SOURCE_URI + value: ":5432/?sslmode=disable" + - name: DATA_SOURCE_USER + value: "postgres" + - name: DATA_SOURCE_PASS + valueFrom: + secretKeyRef: + name: postgres.test-pg.credentials.postgresql.acid.zalan.do + key: password resources: limits: cpu: 500m From 6035fdd58ec1b11b5471cb4ef67eab9e6693f283 Mon Sep 17 00:00:00 2001 From: Felix Kunde Date: Mon, 23 Dec 2024 12:12:33 +0100 Subject: [PATCH 34/34] bump operator to 1.14.0 (#2827) --- charts/postgres-operator-ui/Chart.yaml | 4 +- charts/postgres-operator-ui/index.yaml | 35 +++++++++++++++--- .../postgres-operator-ui-1.14.0.tgz | Bin 0 -> 5082 bytes charts/postgres-operator-ui/values.yaml | 2 +- charts/postgres-operator/Chart.yaml | 2 +- charts/postgres-operator/index.yaml | 34 ++++++++++++++--- .../postgres-operator-1.14.0.tgz | Bin 0 -> 18203 bytes charts/postgres-operator/values.yaml | 2 +- manifests/configmap.yaml | 2 +- manifests/operatorconfiguration.crd.yaml | 2 +- manifests/postgres-operator.yaml | 2 +- ...gresql-operator-default-configuration.yaml | 2 +- pkg/controller/operator_config.go | 2 +- pkg/util/config/config.go | 2 +- ui/app/package.json | 2 +- ui/manifests/deployment.yaml | 2 +- 16 files changed, 70 insertions(+), 25 deletions(-) create mode 100644 charts/postgres-operator-ui/postgres-operator-ui-1.14.0.tgz create mode 100644 charts/postgres-operator/postgres-operator-1.14.0.tgz diff --git a/charts/postgres-operator-ui/Chart.yaml b/charts/postgres-operator-ui/Chart.yaml index 1d5597940..f4e2adf95 100644 --- a/charts/postgres-operator-ui/Chart.yaml +++ b/charts/postgres-operator-ui/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator-ui -version: 1.13.0 -appVersion: 1.13.0 +version: 1.14.0 +appVersion: 1.14.0 home: https://github.com/zalando/postgres-operator description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience keywords: diff --git a/charts/postgres-operator-ui/index.yaml b/charts/postgres-operator-ui/index.yaml index 1b89eeb60..dab9594e9 100644 --- a/charts/postgres-operator-ui/index.yaml +++ b/charts/postgres-operator-ui/index.yaml @@ -1,9 +1,32 @@ apiVersion: v1 entries: postgres-operator-ui: + - apiVersion: v2 + appVersion: 1.14.0 + created: "2024-12-23T11:26:07.721761867+01:00" + description: Postgres Operator UI provides a graphical interface for a convenient + database-as-a-service user experience + digest: e87ed898079a852957a67a4caf3fbd27b9098e413f5d961b7a771a6ae8b3e17c + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - ui + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator-ui + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-ui-1.14.0.tgz + version: 1.14.0 - apiVersion: v2 appVersion: 1.13.0 - created: "2024-08-21T18:55:36.524305158+02:00" + created: "2024-12-23T11:26:07.719409282+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: e0444e516b50f82002d1a733527813c51759a627cefdd1005cea73659f824ea8 @@ -26,7 +49,7 @@ entries: version: 1.13.0 - apiVersion: v2 appVersion: 1.12.2 - created: "2024-08-21T18:55:36.521875733+02:00" + created: "2024-12-23T11:26:07.717202918+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: cbcef400c23ccece27d97369ad629278265c013e0a45c0b7f33e7568a082fedd @@ -49,7 +72,7 @@ entries: version: 1.12.2 - apiVersion: v2 appVersion: 1.11.0 - created: "2024-08-21T18:55:36.51959105+02:00" + created: "2024-12-23T11:26:07.714792146+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: a45f2284045c2a9a79750a36997386444f39b01ac722b17c84b431457577a3a2 @@ -72,7 +95,7 @@ entries: version: 1.11.0 - apiVersion: v2 appVersion: 1.10.1 - created: "2024-08-21T18:55:36.516518177+02:00" + created: "2024-12-23T11:26:07.712194397+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 2e5e7a82aebee519ec57c6243eb8735124aa4585a3a19c66ffd69638fbeb11ce @@ -95,7 +118,7 @@ entries: version: 1.10.1 - apiVersion: v2 appVersion: 1.9.0 - created: "2024-08-21T18:55:36.52712908+02:00" + created: "2024-12-23T11:26:07.723891496+01:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: df434af6c8b697fe0631017ecc25e3c79e125361ae6622347cea41a545153bdc @@ -116,4 +139,4 @@ entries: urls: - postgres-operator-ui-1.9.0.tgz version: 1.9.0 -generated: "2024-08-21T18:55:36.512456099+02:00" +generated: "2024-12-23T11:26:07.709192608+01:00" diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.14.0.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.14.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..8e229d0f5af9edc0c38321b1e267edcec26d3424 GIT binary patch literal 5082 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 z zW}}J#ugWkXTME}i64!B8kX;V@S7+CkSL3tc-~m|c(Z}!}e$7*fhif^1bu+r|UyU!$ ze(aB5oqgMXXoZ`>%Ll8z>W?mOuFm_T@$128@U01mJEFqP5a@#yU7uZj-@n!^8x7CS zkF94T4*GC4{j(oOdvL#ZHogP=y!FL!BqwZ!Ults{K@pOL_-d1YvZxl{C5d0}!Cn(^ z8|4!s^LdVfVmuz0)w* z4@J|g0Q1`Wq2!#W`D~u$(!jPl9{`S0js`63g!{qF;S>Ytx0-uEryQ{)vgvu=exMqS zey|&z-f}PT`R#oO4OkuHKG?Ohd|d2lD%`td3xMr+cCxj~Y46Pm_vWEgYZ^2L&r==?U6{~QdJ=L`ywYmvP z;@3lvszvavM5Yx^9o9$<>#0QF2s@+qeOo|3cGw`cL(tgPDC{4vBv?`;&R}BMabM3Ux zA5=YKql&EGw|cNvwupoH5al(Z%40yP4)wH6nm$pyYJfQzRCDMRwdkC>5#$se7~SSA z^XP+u?5{_QmMT(J-L*ckHsm;^Z)=$o65S$;H=RV4!*2DoPk7Xm_9E@DKv}++AUEYp z(Z0(AmGtjv|M_jdy=DLT&-UMw zq}u&Y<qLbK?>1g(uowtZZL6#ZE!R$fz0S>t(if;%;qi6s87YF?@-? zZsE}9rtR{Ww7nH}?ye3$*Bk=FeKc)@Zy6WsO_uBP;np7M$7~Ef61$tz<~tgtJ4r2o zK7;BxPW~6?6+Ti89FYHB$M-hkKm4HmeE#c6Qa%1}<1onXNWe`sQ3ifJZmi$)D)tEJ z^yx8hJLkWvdVtPY%JDIH?9luVgH8J{41?$S|0L-IhD1u_OkXu*#@t*cG|6caQ#ON) zM7LyyVs!(c$TMw0fS6;Fz${58CiaN3+1J1^(f4ZV^=joiVX<`r3}@!@tS>XYh{f0# zRe$-)g-e#)LCSPVMG|DlA)yRi%Y8W-k7UZxI)U?au}B%bJ|96$xv<E0l_rMOkpyha7C-ATRZZ8ab&9)l`t)>_H zVfXJ|(DOXA)%kl|H>~j*;zq%#z<2k5n0Ee!qkB=n};!1IFGC!J<93_4xE8HIi) zJSFX*6X7(Dea{OcB%NkEJe_uf7Lu;9B_4iN}0iL+xH; zD4mr0Ze#Oi?l%SQJ1Wvc+Sc(DiGd#sA&JlX1mY9)^0HdoGm z(@@JTE@=HyOn!|f5l&m47X{5I!U%&-eA;QYPf6SkPJ>Q2Xor#K zH_;=lF!CmyX|su)u-zqIH<)4^KlAGo@oV4)JNeac;W7N`A17b^)9}>y!apXvl2$kk zf^N%eg;79GN$0c^dZgJtJ?%8R?N+NbZJtg%pG2J?nE0Kj+nThRek<&RoepV|;F(>Y zh+X|X;?+M|yn3#G6uY)Vzw388e@uQ2FvJe&1_URFp?``|*ooRftBI3try2S|i;z~> z4M~&ur%}5-oyP6ccC$O3;KcU}WY7Hi`}3>k`aAj6aN!aBdU~Yw+VO)^?+ZEQRfu>AZ)gMg6(z?G+UhrNy{f;EA+$WB#xfB^=Y{E wbhmI-EV#Orey_X?T4CEeJuNx#bFU?z({p-Gf3Ebu0RRC1{~8%$NdRmB0P<%Gy8r+H literal 0 HcmV?d00001 diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index 22f787826..da3c4baaf 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -8,7 +8,7 @@ replicaCount: 1 image: registry: ghcr.io repository: zalando/postgres-operator-ui - tag: v1.13.0 + tag: v1.14.0 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index 89b6dd15a..35852c488 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator version: 1.14.0 -appVersion: 1.13.0 +appVersion: 1.14.0 home: https://github.com/zalando/postgres-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes keywords: diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index c72604daa..4da98d70a 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -1,9 +1,31 @@ apiVersion: v1 entries: postgres-operator: + - apiVersion: v2 + appVersion: 1.14.0 + created: "2024-12-23T11:25:32.596716566+01:00" + description: Postgres Operator creates and manages PostgreSQL clusters running + in Kubernetes + digest: 36e1571f3f455b213f16cdda7b1158648e8e84deb804ba47ed6b9b6d19263ba8 + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-1.14.0.tgz + version: 1.14.0 - apiVersion: v2 appVersion: 1.13.0 - created: "2024-08-21T18:54:43.160735116+02:00" + created: "2024-12-23T11:25:32.591136261+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: a839601689aea0a7e6bc0712a5244d435683cf3314c95794097ff08540e1dfef @@ -25,7 +47,7 @@ entries: version: 1.13.0 - apiVersion: v2 appVersion: 1.12.2 - created: "2024-08-21T18:54:43.152249286+02:00" + created: "2024-12-23T11:25:32.585419709+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 65858d14a40d7fd90c32bd9fc60021acc9555c161079f43a365c70171eaf21d8 @@ -47,7 +69,7 @@ entries: version: 1.12.2 - apiVersion: v2 appVersion: 1.11.0 - created: "2024-08-21T18:54:43.145837894+02:00" + created: "2024-12-23T11:25:32.580077286+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 3914b5e117bda0834f05c9207f007e2ac372864cf6e86dcc2e1362bbe46c14d9 @@ -69,7 +91,7 @@ entries: version: 1.11.0 - apiVersion: v2 appVersion: 1.10.1 - created: "2024-08-21T18:54:43.139552116+02:00" + created: "2024-12-23T11:25:32.574641578+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: cc3baa41753da92466223d0b334df27e79c882296577b404a8e9071411fcf19c @@ -91,7 +113,7 @@ entries: version: 1.10.1 - apiVersion: v2 appVersion: 1.9.0 - created: "2024-08-21T18:54:43.168490032+02:00" + created: "2024-12-23T11:25:32.604748814+01:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 64df90c898ca591eb3a330328173ffaadfbf9ddd474d8c42ed143edc9e3f4276 @@ -111,4 +133,4 @@ entries: urls: - postgres-operator-1.9.0.tgz version: 1.9.0 -generated: "2024-08-21T18:54:43.126871802+02:00" +generated: "2024-12-23T11:25:32.568598763+01:00" diff --git a/charts/postgres-operator/postgres-operator-1.14.0.tgz b/charts/postgres-operator/postgres-operator-1.14.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..df95fd01d556a0b8cf3e8f8ca6ba9bf487dca4f5 GIT binary patch literal 18203 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 SKmR8H0RR8piF)b)z5xKey}ES( literal 0 HcmV?d00001 diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 881ff05d6..2511a09d3 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -1,7 +1,7 @@ image: registry: ghcr.io repository: zalando/postgres-operator - tag: v1.13.0 + tag: v1.14.0 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 094bd6bd5..9473ef5ec 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -86,7 +86,7 @@ data: # logical_backup_cpu_limit: "" # logical_backup_cpu_request: "" logical_backup_cronjob_environment_secret: "" - logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.13.0" + logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" # logical_backup_memory_limit: "" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index d4990bf2b..ded2477d7 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -508,7 +508,7 @@ spec: pattern: '^(\d+m|\d+(\.\d{1,3})?)$' logical_backup_docker_image: type: string - default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.13.0" + default: "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: diff --git a/manifests/postgres-operator.yaml b/manifests/postgres-operator.yaml index fbba84c7f..e3f77657e 100644 --- a/manifests/postgres-operator.yaml +++ b/manifests/postgres-operator.yaml @@ -19,7 +19,7 @@ spec: serviceAccountName: postgres-operator containers: - name: postgres-operator - image: ghcr.io/zalando/postgres-operator:v1.13.0 + image: ghcr.io/zalando/postgres-operator:v1.14.0 imagePullPolicy: IfNotPresent resources: requests: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index db0d13b5f..570ebd338 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -168,7 +168,7 @@ configuration: # logical_backup_cpu_request: "" # logical_backup_memory_limit: "" # logical_backup_memory_request: "" - logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.13.0" + logical_backup_docker_image: "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" logical_backup_provider: "s3" diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index ba347b2fd..5739f6314 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -180,7 +180,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // logical backup config result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *") - result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "ghcr.io/zalando/postgres-operator/logical-backup:v1.13.0") + result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0") result.LogicalBackupProvider = util.Coalesce(fromCRD.LogicalBackup.BackupProvider, "s3") result.LogicalBackupAzureStorageAccountName = fromCRD.LogicalBackup.AzureStorageAccountName result.LogicalBackupAzureStorageAccountKey = fromCRD.LogicalBackup.AzureStorageAccountKey diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 6c76718b7..30b967beb 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -127,7 +127,7 @@ type Scalyr struct { // LogicalBackup defines configuration for logical backup type LogicalBackup struct { LogicalBackupSchedule string `name:"logical_backup_schedule" default:"30 00 * * *"` - LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"ghcr.io/zalando/postgres-operator/logical-backup:v1.13.0"` + LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"ghcr.io/zalando/postgres-operator/logical-backup:v1.14.0"` LogicalBackupProvider string `name:"logical_backup_provider" default:"s3"` LogicalBackupAzureStorageAccountName string `name:"logical_backup_azure_storage_account_name" default:""` LogicalBackupAzureStorageContainer string `name:"logical_backup_azure_storage_container" default:""` diff --git a/ui/app/package.json b/ui/app/package.json index e96ee77dc..ef24834ca 100644 --- a/ui/app/package.json +++ b/ui/app/package.json @@ -1,6 +1,6 @@ { "name": "postgres-operator-ui", - "version": "1.13.0", + "version": "1.14.0", "description": "PostgreSQL Operator UI", "main": "src/app.js", "config": { diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index 9b0038579..e09dd1e4f 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -18,7 +18,7 @@ spec: serviceAccountName: postgres-operator-ui containers: - name: "service" - image: ghcr.io/zalando/postgres-operator-ui:v1.13.0 + image: ghcr.io/zalando/postgres-operator-ui:v1.14.0 ports: - containerPort: 8081 protocol: "TCP"