mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-08 03:42:04 +02:00
Full AWS gp3 support for iops and througput config. (#1261)
Support new AWS EBS volume type `gp3` with `iops` and `throughput` in the manifest. Co-authored-by: Felix Kunde <felix-kunde@gmx.de>
This commit is contained in:
co-authored by
Felix Kunde
parent
4ea0b5f432
commit
4a88f00a3f
+253
-52
@@ -11,7 +11,9 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zalando/postgres-operator/mocks"
|
||||
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
@@ -187,60 +189,16 @@ func TestMigrateEBS(t *testing.T) {
|
||||
cluster.Namespace = namespace
|
||||
filterLabels := cluster.labelsSet(false)
|
||||
|
||||
pvcList := CreatePVCs(namespace, clusterName, filterLabels, 2, "1Gi")
|
||||
|
||||
ps := v1.PersistentVolumeSpec{}
|
||||
ps.AWSElasticBlockStore = &v1.AWSElasticBlockStoreVolumeSource{}
|
||||
ps.AWSElasticBlockStore.VolumeID = "aws://eu-central-1b/ebs-volume-1"
|
||||
|
||||
ps2 := v1.PersistentVolumeSpec{}
|
||||
ps2.AWSElasticBlockStore = &v1.AWSElasticBlockStoreVolumeSource{}
|
||||
ps2.AWSElasticBlockStore.VolumeID = "aws://eu-central-1b/ebs-volume-2"
|
||||
|
||||
pvList := &v1.PersistentVolumeList{
|
||||
Items: []v1.PersistentVolume{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "persistent-volume-0",
|
||||
},
|
||||
Spec: ps,
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "persistent-volume-1",
|
||||
},
|
||||
Spec: ps2,
|
||||
},
|
||||
testVolumes := []testVolume{
|
||||
{
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
size: 100,
|
||||
},
|
||||
}
|
||||
|
||||
for _, pvc := range pvcList.Items {
|
||||
cluster.KubeClient.PersistentVolumeClaims(namespace).Create(context.TODO(), &pvc, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
for _, pv := range pvList.Items {
|
||||
cluster.KubeClient.PersistentVolumes().Create(context.TODO(), &pv, metav1.CreateOptions{})
|
||||
}
|
||||
|
||||
pod := v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName + "-0",
|
||||
Labels: filterLabels,
|
||||
},
|
||||
Spec: v1.PodSpec{},
|
||||
}
|
||||
|
||||
cluster.KubeClient.Pods(namespace).Create(context.TODO(), &pod, metav1.CreateOptions{})
|
||||
|
||||
pod = v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: clusterName + "-1",
|
||||
Labels: filterLabels,
|
||||
},
|
||||
Spec: v1.PodSpec{},
|
||||
}
|
||||
|
||||
cluster.KubeClient.Pods(namespace).Create(context.TODO(), &pod, metav1.CreateOptions{})
|
||||
initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
@@ -256,8 +214,251 @@ func TestMigrateEBS(t *testing.T) {
|
||||
{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("gp3"), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||||
resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-1"), gomock.Eq(aws.String("gp3")), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||||
|
||||
cluster.VolumeResizer = resizer
|
||||
cluster.executeEBSMigration()
|
||||
}
|
||||
|
||||
type testVolume struct {
|
||||
iops int64
|
||||
throughtput int64
|
||||
size int64
|
||||
volType string
|
||||
}
|
||||
|
||||
func initTestVolumesAndPods(client k8sutil.KubernetesClient, namespace, clustername string, labels labels.Set, volumes []testVolume) {
|
||||
i := 0
|
||||
for _, v := range volumes {
|
||||
storage1Gi, _ := resource.ParseQuantity(fmt.Sprintf("%d", v.size))
|
||||
|
||||
ps := v1.PersistentVolumeSpec{}
|
||||
ps.AWSElasticBlockStore = &v1.AWSElasticBlockStoreVolumeSource{}
|
||||
ps.AWSElasticBlockStore.VolumeID = fmt.Sprintf("aws://eu-central-1b/ebs-volume-%d", i+1)
|
||||
|
||||
pv := v1.PersistentVolume{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("persistent-volume-%d", i),
|
||||
},
|
||||
Spec: ps,
|
||||
}
|
||||
|
||||
client.PersistentVolumes().Create(context.TODO(), &pv, metav1.CreateOptions{})
|
||||
|
||||
pvc := v1.PersistentVolumeClaim{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%s-%d", constants.DataVolumeName, clustername, i),
|
||||
Namespace: namespace,
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.PersistentVolumeClaimSpec{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceStorage: storage1Gi,
|
||||
},
|
||||
},
|
||||
VolumeName: fmt.Sprintf("persistent-volume-%d", i),
|
||||
},
|
||||
}
|
||||
|
||||
client.PersistentVolumeClaims(namespace).Create(context.TODO(), &pvc, metav1.CreateOptions{})
|
||||
|
||||
pod := v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: fmt.Sprintf("%s-%d", clustername, i),
|
||||
Labels: labels,
|
||||
},
|
||||
Spec: v1.PodSpec{},
|
||||
}
|
||||
|
||||
client.Pods(namespace).Create(context.TODO(), &pod, metav1.CreateOptions{})
|
||||
|
||||
i = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
func TestMigrateGp3Support(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)
|
||||
|
||||
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{
|
||||
{
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
size: 100,
|
||||
},
|
||||
}
|
||||
|
||||
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().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)
|
||||
|
||||
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{
|
||||
{
|
||||
size: 100,
|
||||
},
|
||||
{
|
||||
size: 100,
|
||||
},
|
||||
}
|
||||
|
||||
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().DescribeVolumes(gomock.Eq([]string{"ebs-volume-1", "ebs-volume-2"})).Return(
|
||||
[]volumes.VolumeProperties{
|
||||
{VolumeID: "ebs-volume-1", VolumeType: "gp2", Size: 150, Iops: 3000},
|
||||
{VolumeID: "ebs-volume-2", VolumeType: "gp2", Size: 150, Iops: 4000},
|
||||
}, nil)
|
||||
|
||||
// expect only gp2 volume to be modified
|
||||
resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-1"), gomock.Eq(aws.String("gp3")), gomock.Nil(), 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.Nil(), gomock.Eq(aws.Int64(6000)), gomock.Eq(aws.Int64(275))).Return(nil)
|
||||
|
||||
cluster.VolumeResizer = resizer
|
||||
cluster.syncVolumes()
|
||||
}
|
||||
|
||||
func TestDontTouchType(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)
|
||||
|
||||
cluster.Spec.Volume.Size = "177Gi"
|
||||
|
||||
// set metadata, so that labels will get correct values
|
||||
cluster.Name = clusterName
|
||||
cluster.Namespace = namespace
|
||||
filterLabels := cluster.labelsSet(false)
|
||||
|
||||
testVolumes := []testVolume{
|
||||
{
|
||||
size: 150,
|
||||
},
|
||||
{
|
||||
size: 150,
|
||||
},
|
||||
}
|
||||
|
||||
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().DescribeVolumes(gomock.Eq([]string{"ebs-volume-1", "ebs-volume-2"})).Return(
|
||||
[]volumes.VolumeProperties{
|
||||
{VolumeID: "ebs-volume-1", VolumeType: "gp2", Size: 150, Iops: 3000},
|
||||
{VolumeID: "ebs-volume-2", VolumeType: "gp2", Size: 150, Iops: 4000},
|
||||
}, nil)
|
||||
|
||||
// expect only gp2 volume to be modified
|
||||
resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-1"), gomock.Nil(), gomock.Eq(aws.Int64(177)), gomock.Nil(), gomock.Nil()).Return(nil)
|
||||
resizer.EXPECT().ModifyVolume(gomock.Eq("ebs-volume-2"), gomock.Nil(), gomock.Eq(aws.Int64(177)), gomock.Nil(), gomock.Nil()).Return(nil)
|
||||
|
||||
cluster.VolumeResizer = resizer
|
||||
cluster.syncVolumes()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user