remove gp3 migration flow (#3142)

This commit is contained in:
Felix Kunde
2026-07-27 14:02:44 +02:00
committed by GitHub
parent bffde0f5e7
commit 56a8f1d90b
17 changed files with 31 additions and 260 deletions
@@ -246,17 +246,15 @@ type LoadBalancerConfiguration struct {
type AWSGCPConfiguration struct {
WALES3Bucket string `json:"wal_s3_bucket,omitempty"`
// +kubebuilder:default=eu-central-1
AWSRegion string `json:"aws_region,omitempty"`
WALGSBucket string `json:"wal_gs_bucket,omitempty"`
GCPCredentials string `json:"gcp_credentials,omitempty"`
WALAZStorageAccount string `json:"wal_az_storage_account,omitempty"`
LogS3Bucket string `json:"log_s3_bucket,omitempty"`
KubeIAMRole string `json:"kube_iam_role,omitempty"`
IRSARoleARN string `json:"irsa_role_arn,omitempty"`
AdditionalSecretMount string `json:"additional_secret_mount,omitempty"`
AdditionalSecretMountPath string `json:"additional_secret_mount_path,omitempty"`
EnableEBSGp3Migration bool `json:"enable_ebs_gp3_migration,omitempty"`
EnableEBSGp3MigrationMaxSize int64 `json:"enable_ebs_gp3_migration_max_size,omitempty"`
AWSRegion string `json:"aws_region,omitempty"`
WALGSBucket string `json:"wal_gs_bucket,omitempty"`
GCPCredentials string `json:"gcp_credentials,omitempty"`
WALAZStorageAccount string `json:"wal_az_storage_account,omitempty"`
LogS3Bucket string `json:"log_s3_bucket,omitempty"`
KubeIAMRole string `json:"kube_iam_role,omitempty"`
IRSARoleARN string `json:"irsa_role_arn,omitempty"`
AdditionalSecretMount string `json:"additional_secret_mount,omitempty"`
AdditionalSecretMountPath string `json:"additional_secret_mount_path,omitempty"`
}
// OperatorDebugConfiguration defines options for the debug mode
@@ -66,11 +66,6 @@ spec:
aws_region:
default: eu-central-1
type: string
enable_ebs_gp3_migration:
type: boolean
enable_ebs_gp3_migration_max_size:
format: int64
type: integer
gcp_credentials:
type: string
irsa_role_arn:
+1 -1
View File
@@ -176,7 +176,7 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres
cluster.eventRecorder = eventRecorder
cluster.EBSVolumes = make(map[string]volumes.VolumeProperties)
if cfg.OpConfig.StorageResizeMode != "pvc" || cfg.OpConfig.EnableEBSGp3Migration {
if cfg.OpConfig.StorageResizeMode != "pvc" {
cluster.VolumeResizer = &volumes.EBSVolumeResizer{AWSRegion: cfg.OpConfig.AWSRegion}
}
-9
View File
@@ -95,18 +95,10 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
c.logger.Errorf("could not sync Patroni resources: %v", err)
}
// sync volume may already transition volumes to gp3, if iops/throughput or type is specified
if err = c.syncVolumes(); err != nil {
return err
}
if c.OpConfig.EnableEBSGp3Migration && len(c.EBSVolumes) > 0 {
err = c.executeEBSMigration()
if nil != err {
return err
}
}
if err = c.syncPodServiceAccount(); err != nil {
c.logger.Errorf("could not sync pod service account: %v", err)
}
@@ -1811,7 +1803,6 @@ func (c *Cluster) syncLogicalBackupJob() error {
// no existing logical backup job, create new one
c.logger.Info("could not find the cluster's logical backup job")
if err = c.createLogicalBackupJob(); err == nil {
c.logger.Infof("created missing logical backup job %s", jobName)
} else {
+4 -61
View File
@@ -11,7 +11,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/zalando/postgres-operator/pkg/spec"
"github.com/zalando/postgres-operator/pkg/util/constants"
"github.com/zalando/postgres-operator/pkg/util/filesystems"
@@ -79,9 +78,6 @@ func (c *Cluster) syncUnderlyingEBSVolume() error {
}
targetSize := quantityToGigabyte(newSize)
awsGp3 := aws.String("gp3")
awsIo2 := aws.String("io2")
errors := make([]string, 0)
for _, volume := range c.EBSVolumes {
@@ -106,20 +102,11 @@ func (c *Cluster) syncUnderlyingEBSVolume() error {
modifySize = &targetSize
}
if modifyIops != nil || modifyThroughput != nil || modifySize != nil {
if modifyIops != nil || modifyThroughput != nil {
// we default to gp3 if iops and throughput are configured
modifyType = awsGp3
if targetValue.VolumeType == "io2" {
modifyType = awsIo2
}
} else if targetValue.VolumeType == "gp3" && volume.VolumeType != "gp3" {
modifyType = awsGp3
} else {
// do not touch type
modifyType = nil
}
if targetValue.VolumeType != "" && targetValue.VolumeType != volume.VolumeType {
modifyType = &targetValue.VolumeType
}
if modifyIops != nil || modifyThroughput != nil || modifySize != nil || modifyType != nil {
err = c.VolumeResizer.ModifyVolume(volume.VolumeID, modifyType, modifySize, modifyIops, modifyThroughput)
if err != nil {
errors = append(errors, fmt.Sprintf("modify failed: %v, showing current EBS values: volume-id=%s size=%d iops=%d throughput=%d", err, volume.VolumeID, volume.Size, volume.Iops, volume.Throughput))
@@ -453,47 +440,3 @@ func getPodNameFromPersistentVolume(pv *v1.PersistentVolume) *spec.NamespacedNam
func quantityToGigabyte(q resource.Quantity) int64 {
return q.ScaledValue(0) / (1 * constants.Gigabyte)
}
func (c *Cluster) executeEBSMigration() error {
pvs, err := c.listPersistentVolumes()
if err != nil {
return fmt.Errorf("could not list persistent volumes: %v", err)
}
if len(pvs) == 0 {
c.logger.Warningf("no persistent volumes found - skipping EBS migration")
return nil
}
c.logger.Debugf("found %d volumes, size of known volumes %d", len(pvs), len(c.EBSVolumes))
if len(pvs) == len(c.EBSVolumes) {
hasGp2 := false
for _, v := range c.EBSVolumes {
if v.VolumeType == "gp2" {
hasGp2 = true
}
}
if !hasGp2 {
c.logger.Debugf("no EBS gp2 volumes left to migrate")
return nil
}
}
var i3000 int64 = 3000
var i125 int64 = 125
for _, volume := range c.EBSVolumes {
if volume.VolumeType == "gp2" && volume.Size < c.OpConfig.EnableEBSGp3MigrationMaxSize {
c.logger.Infof("modifying EBS volume %s to type gp3 migration (%d)", volume.VolumeID, volume.Size)
err = c.VolumeResizer.ModifyVolume(volume.VolumeID, aws.String("gp3"), &volume.Size, &i3000, &i125)
if nil != err {
c.logger.Warningf("modifying volume %s failed: %v", volume.VolumeID, err)
}
} else {
c.logger.Debugf("skipping EBS volume %s to type gp3 migration (%d)", volume.VolumeID, volume.Size)
}
c.EBSVolumes[volume.VolumeID] = volume
}
return nil
}
+5 -129
View File
@@ -179,64 +179,6 @@ func CreatePVCs(namespace string, clusterName string, labels labels.Set, n int,
return pvcList
}
func TestMigrateEBS(t *testing.T) {
client, _ := newFakeK8sPVCclient()
clusterName := "acid-test-cluster"
namespace := "default"
// new cluster with pvc storage resize mode and configured labels
var cluster = New(
Config{
OpConfig: config.Config{
Resources: config.Resources{
ClusterLabels: map[string]string{"application": "spilo"},
ClusterNameLabel: "cluster-name",
},
StorageResizeMode: "pvc",
EnableEBSGp3Migration: true,
EnableEBSGp3MigrationMaxSize: 1000,
},
}, client, acidv1.Postgresql{}, logger, eventRecorder)
cluster.Spec.Volume.Size = "1Gi"
// set metadata, so that labels will get correct values
cluster.Name = clusterName
cluster.Namespace = namespace
filterLabels := cluster.labelsSet(false)
testVolumes := []testVolume{testVol, testVol}
initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
resizer := mocks.NewMockVolumeResizer(ctrl)
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},
{VolumeID: "ebs-volume-2", VolumeType: "gp3", Size: 100}}, nil)
// expect only gp2 volume to be modified
resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-1"), gomock.Eq(aws.String("gp3")), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
cluster.VolumeResizer = resizer
err := cluster.populateVolumeMetaData()
assert.NoError(t, err)
err = cluster.executeEBSMigration()
assert.NoError(t, err)
}
func initTestVolumesAndPods(client k8sutil.KubernetesClient, namespace, clustername string, labels labels.Set, volumes []testVolume) {
i := 0
for _, v := range volumes {
@@ -287,7 +229,7 @@ func initTestVolumesAndPods(client k8sutil.KubernetesClient, namespace, clustern
}
}
func TestMigrateGp3Support(t *testing.T) {
func TestGp2Gp3Migration(t *testing.T) {
client, _ := newFakeK8sPVCclient()
clusterName := "acid-test-cluster"
namespace := "default"
@@ -300,73 +242,9 @@ func TestMigrateGp3Support(t *testing.T) {
ClusterLabels: map[string]string{"application": "spilo"},
ClusterNameLabel: "cluster-name",
},
StorageResizeMode: "mixed",
EnableEBSGp3Migration: false,
EnableEBSGp3MigrationMaxSize: 1000,
StorageResizeMode: "mixed",
},
}, client, acidv1.Postgresql{}, logger, eventRecorder)
cluster.Spec.Volume.Size = "150Gi"
cluster.Spec.Volume.Iops = aws.Int64(6000)
cluster.Spec.Volume.Throughput = aws.Int64(275)
// set metadata, so that labels will get correct values
cluster.Name = clusterName
cluster.Namespace = namespace
filterLabels := cluster.labelsSet(false)
testVolumes := []testVolume{testVol, testVol, testVol}
initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
ctrl := gomock.NewController(t)
defer ctrl.Finish()
resizer := mocks.NewMockVolumeResizer(ctrl)
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().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},
{VolumeID: "ebs-volume-2", VolumeType: "gp3", Size: 105, Iops: 4000},
{VolumeID: "ebs-volume-3", VolumeType: "gp3", Size: 151, Iops: 6000, Throughput: 275}}, nil)
// expect only gp2 volume to be modified
resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-1"), gomock.Eq(aws.String("gp3")), gomock.Eq(aws.Int64(150)), gomock.Eq(aws.Int64(6000)), gomock.Eq(aws.Int64(275))).Return(nil)
resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-2"), gomock.Eq(aws.String("gp3")), gomock.Eq(aws.Int64(150)), gomock.Eq(aws.Int64(6000)), gomock.Eq(aws.Int64(275))).Return(nil)
// resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-3"), gomock.Eq(aws.String("gp3")), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
cluster.VolumeResizer = resizer
cluster.syncVolumes()
}
func TestManualGp2Gp3Support(t *testing.T) {
client, _ := newFakeK8sPVCclient()
clusterName := "acid-test-cluster"
namespace := "default"
// new cluster with pvc storage resize mode and configured labels
var cluster = New(
Config{
OpConfig: config.Config{
Resources: config.Resources{
ClusterLabels: map[string]string{"application": "spilo"},
ClusterNameLabel: "cluster-name",
},
StorageResizeMode: "mixed",
EnableEBSGp3Migration: false,
EnableEBSGp3MigrationMaxSize: 1000,
},
}, client, acidv1.Postgresql{}, logger, eventRecorder)
}, client, acidv1.Postgresql{Spec: acidv1.PostgresSpec{Volume: acidv1.Volume{VolumeType: "gp3"}}}, logger, eventRecorder)
cluster.Spec.Volume.Size = "150Gi"
cluster.Spec.Volume.Iops = aws.Int64(6000)
@@ -409,7 +287,7 @@ func TestManualGp2Gp3Support(t *testing.T) {
cluster.syncVolumes()
}
func TestDontTouchType(t *testing.T) {
func TestNoVolumeTypeChange(t *testing.T) {
client, _ := newFakeK8sPVCclient()
clusterName := "acid-test-cluster"
namespace := "default"
@@ -422,9 +300,7 @@ func TestDontTouchType(t *testing.T) {
ClusterLabels: map[string]string{"application": "spilo"},
ClusterNameLabel: "cluster-name",
},
StorageResizeMode: "mixed",
EnableEBSGp3Migration: false,
EnableEBSGp3MigrationMaxSize: 1000,
StorageResizeMode: "mixed",
},
}, client, acidv1.Postgresql{}, logger, eventRecorder)
-2
View File
@@ -181,8 +181,6 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.WALAZStorageAccount = fromCRD.AWSGCP.WALAZStorageAccount
result.AdditionalSecretMount = fromCRD.AWSGCP.AdditionalSecretMount
result.AdditionalSecretMountPath = fromCRD.AWSGCP.AdditionalSecretMountPath
result.EnableEBSGp3Migration = fromCRD.AWSGCP.EnableEBSGp3Migration
result.EnableEBSGp3MigrationMaxSize = util.CoalesceInt64(fromCRD.AWSGCP.EnableEBSGp3MigrationMaxSize, 1000)
// logical backup config
result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *")
-2
View File
@@ -200,8 +200,6 @@ type Config struct {
WALAZStorageAccount string `name:"wal_az_storage_account"`
AdditionalSecretMount string `name:"additional_secret_mount"`
AdditionalSecretMountPath string `name:"additional_secret_mount_path"`
EnableEBSGp3Migration bool `name:"enable_ebs_gp3_migration" default:"false"`
EnableEBSGp3MigrationMaxSize int64 `name:"enable_ebs_gp3_migration_max_size" default:"1000"`
DebugLogging bool `name:"debug_logging" default:"true"`
EnableDBAccess bool `name:"enable_database_access" default:"true"`
EnableTeamsAPI bool `name:"enable_teams_api" default:"true"`
+7 -7
View File
@@ -89,14 +89,14 @@ func (r *EBSVolumeResizer) DescribeVolumes(volumeIds []string) ([]VolumeProperti
}
for _, v := range volumeOutput.Volumes {
switch v.VolumeType {
case "gp3":
p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: int64(*v.Size), VolumeType: string(v.VolumeType), Iops: int64(*v.Iops), Throughput: int64(*v.Throughput)})
case "gp2":
p = append(p, VolumeProperties{VolumeID: *v.VolumeId, Size: int64(*v.Size), VolumeType: string(v.VolumeType)})
default:
return nil, fmt.Errorf("discovered unexpected volume type %s %s", *v.VolumeId, v.VolumeType)
vp := VolumeProperties{VolumeID: *v.VolumeId, Size: int64(*v.Size), VolumeType: string(v.VolumeType)}
if v.Iops != nil {
vp.Iops = int64(*v.Iops)
}
if v.Throughput != nil {
vp.Throughput = int64(*v.Throughput)
}
p = append(p, vp)
}
return p, nil