Skip to content

Commit bf4ca05

Browse files
committed
PR Feedback
Issue: [sc-17759]
1 parent 499d3c4 commit bf4ca05

File tree

15 files changed

+90
-84
lines changed

15 files changed

+90
-84
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ deploy-dev: createnamespaces
138138
hack/create-kubeconfig.sh postgres-operator pgo
139139
env \
140140
CRUNCHY_DEBUG=true \
141+
PGO_FEATURE_GATES="TablespaceVolumes=true" \
141142
CHECK_FOR_UPGRADES='$(if $(CHECK_FOR_UPGRADES),$(CHECK_FOR_UPGRADES),false)' \
142143
KUBECONFIG=hack/.kube/postgres-operator/pgo \
143144
PGO_NAMESPACE='postgres-operator' \

config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -9645,12 +9645,17 @@ spec:
96459645
description: The name for the tablespace, used as the
96469646
path name for the volume. Must be unique in the instance
96479647
set since they become the directory names.
9648+
minLength: 1
9649+
pattern: ^[a-z][a-z0-9]*$
96489650
type: string
96499651
required:
96509652
- dataVolumeClaimSpec
96519653
- name
96529654
type: object
96539655
type: array
9656+
x-kubernetes-list-map-keys:
9657+
- name
9658+
x-kubernetes-list-type: map
96549659
tolerations:
96559660
description: 'Tolerations of a PostgreSQL pod. Changing this
96569661
value causes PostgreSQL to restart. More info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration'

docs/content/guides/tablespaces.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ distributed system, there are several considerations to ensure proper operations
2929

3030
- Each tablespace must have its own volume; this means that every tablespace for
3131
every replica in a system must have its own volume;
32-
- The filesystem map must be consistent across the cluster;
32+
- The available filesystem paths must be consistent on each Postgres pod in a Postgres cluster;
3333
- The backup & disaster recovery management system must be able to safely backup
3434
and restore data to tablespaces.
3535

@@ -51,7 +51,7 @@ PGO feature gates are enabled by setting the `PGO_FEATURE_GATES` environment
5151
variable on the PGO Deployment. To enable tablespaces, you would want to set
5252

5353
```
54-
PGO_FEATURE_GATES="TablespaveVolumes=true"
54+
PGO_FEATURE_GATES="TablespaceVolumes=true"
5555
```
5656

5757
Please note that it is possible to enable more than one feature at a time as
@@ -62,7 +62,7 @@ you would set `PGO_FEATURE_GATES` like so:
6262
PGO_FEATURE_GATES="FeatureName=true,FeatureName2=true,FeatureName3=true..."
6363
```
6464

65-
## Using TablespaceVolumes in PGO v5
65+
## Adding TablespaceVolumes to a postgrescluster in PGO v5
6666

6767
Once you have enabled `TablespaceVolumes` on your PGO deployment, you can add volumes to
6868
a new or existing cluster by adding volumes to the `spec.instances.tablespaceVolumes` field.
@@ -122,8 +122,12 @@ spec:
122122
volumeName: pvc-3fea1531-617a-4fff-9032-6487206ce644 # A preexisting volume you want to use for this tablespace
123123
```
124124

125-
Note: the `name` of the `tablespaceVolume` needs to be unique in the instance since
126-
that name becomes part of the mount path for that volume.
125+
Note: the `name` of the `tablespaceVolume` needs to be
126+
127+
* unique in the instance since that name becomes part of the mount path for that volume;
128+
* valid as part of a path name, label, and part of a volume name.
129+
130+
There is validation on the CRD for these requirements.
127131

128132
Once you request those `tablespaceVolumes`, PGO takes care of creating (or reusing) those volumes,
129133
including mounting them to the pod at a known path (`/tablespaces/NAME`) and adding them to the

internal/controller/postgrescluster/pgbackrest.go

+19-27
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ import (
5050
"github.com/crunchydata/postgres-operator/internal/pgbackrest"
5151
"github.com/crunchydata/postgres-operator/internal/pki"
5252
"github.com/crunchydata/postgres-operator/internal/postgres"
53-
"github.com/crunchydata/postgres-operator/internal/util"
5453
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
5554
)
5655

@@ -1118,21 +1117,19 @@ func (r *Reconciler) reconcileRestoreJob(ctx context.Context,
11181117
volumeMounts = append(volumeMounts, walVolumeMount)
11191118
}
11201119

1121-
if util.DefaultMutableFeatureGate.Enabled(util.TablespaceVolumes) {
1122-
for _, pgtablespaceVolume := range pgtablespaceVolumes {
1123-
tablespaceVolumeMount := postgres.TablespaceVolumeMount(
1124-
pgtablespaceVolume.Labels["postgres-operator.crunchydata.com/data"])
1125-
tablespaceVolume := corev1.Volume{
1126-
Name: tablespaceVolumeMount.Name,
1127-
VolumeSource: corev1.VolumeSource{
1128-
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1129-
ClaimName: pgtablespaceVolume.GetName(),
1130-
},
1120+
for _, pgtablespaceVolume := range pgtablespaceVolumes {
1121+
tablespaceVolumeMount := postgres.TablespaceVolumeMount(
1122+
pgtablespaceVolume.Labels[naming.LabelData])
1123+
tablespaceVolume := corev1.Volume{
1124+
Name: tablespaceVolumeMount.Name,
1125+
VolumeSource: corev1.VolumeSource{
1126+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
1127+
ClaimName: pgtablespaceVolume.GetName(),
11311128
},
1132-
}
1133-
volumes = append(volumes, tablespaceVolume)
1134-
volumeMounts = append(volumeMounts, tablespaceVolumeMount)
1129+
},
11351130
}
1131+
volumes = append(volumes, tablespaceVolume)
1132+
volumeMounts = append(volumeMounts, tablespaceVolumeMount)
11361133
}
11371134

11381135
restoreJob := &batchv1.Job{}
@@ -1528,12 +1525,9 @@ func (r *Reconciler) reconcilePostgresClusterDataSource(ctx context.Context,
15281525
return errors.WithStack(err)
15291526
}
15301527

1531-
pgtablespaces := []*corev1.PersistentVolumeClaim{}
1532-
if util.DefaultMutableFeatureGate.Enabled(util.TablespaceVolumes) {
1533-
pgtablespaces, err = r.reconcileTablespaceVolumes(ctx, cluster, instanceSet, fakeSTS, clusterVolumes)
1534-
if err != nil {
1535-
return errors.WithStack(err)
1536-
}
1528+
pgtablespaces, err := r.reconcileTablespaceVolumes(ctx, cluster, instanceSet, fakeSTS, clusterVolumes)
1529+
if err != nil {
1530+
return errors.WithStack(err)
15371531
}
15381532

15391533
// reconcile the pgBackRest restore Job to populate the cluster's data directory
@@ -1626,14 +1620,12 @@ func (r *Reconciler) reconcileCloudBasedDataSource(ctx context.Context,
16261620
return errors.WithStack(err)
16271621
}
16281622

1629-
// TODO(benjaminjb): do we really need this?
1630-
pgtablespaces := []*corev1.PersistentVolumeClaim{}
1631-
if util.DefaultMutableFeatureGate.Enabled(util.TablespaceVolumes) {
1632-
pgtablespaces, err = r.reconcileTablespaceVolumes(ctx, cluster, instanceSet, fakeSTS, clusterVolumes)
1633-
if err != nil {
1634-
return errors.WithStack(err)
1635-
}
1623+
// TODO(benjaminjb): do we really need this for cloud-based datasources?
1624+
pgtablespaces, err := r.reconcileTablespaceVolumes(ctx, cluster, instanceSet, fakeSTS, clusterVolumes)
1625+
if err != nil {
1626+
return errors.WithStack(err)
16361627
}
1628+
16371629
// The `reconcileRestoreJob` was originally designed to take a PostgresClusterDataSource
16381630
// and rather than reconfigure that func's signature, we translate the PGBackRestDataSource
16391631
tmpDataSource := &v1beta1.PostgresClusterDataSource{

internal/naming/names.go

-2
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,6 @@ func InstancePostgresDataVolume(instance *appsv1.StatefulSet) metav1.ObjectMeta
325325
}
326326
}
327327

328-
// TODO(benjaminjb): We need to consider shortening the PVC name
329-
// or at least need to document so that people can avoid too-long names
330328
// InstanceTablespaceDataVolume returns the ObjectMeta for the tablespace data
331329
// volume for instance.
332330
func InstanceTablespaceDataVolume(instance *appsv1.StatefulSet, tablespaceName string) metav1.ObjectMeta {

internal/pgbackrest/config.go

+4-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"github.com/crunchydata/postgres-operator/internal/initialize"
2828
"github.com/crunchydata/postgres-operator/internal/naming"
2929
"github.com/crunchydata/postgres-operator/internal/postgres"
30-
"github.com/crunchydata/postgres-operator/internal/util"
3130
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
3231
)
3332

@@ -207,12 +206,10 @@ func RestoreCommand(pgdata string, tablespaceVolumes []*corev1.PersistentVolumeC
207206
// are no timeouts when starting or stopping Postgres.
208207

209208
tablespaceCmd := ""
210-
if util.DefaultMutableFeatureGate.Enabled(util.TablespaceVolumes) {
211-
for _, tablespaceVolume := range tablespaceVolumes {
212-
tablespaceCmd = tablespaceCmd + fmt.Sprintf(
213-
"\ninstall --directory --mode=0700 '/tablespaces/%s/data'",
214-
tablespaceVolume.Labels["postgres-operator.crunchydata.com/data"])
215-
}
209+
for _, tablespaceVolume := range tablespaceVolumes {
210+
tablespaceCmd = tablespaceCmd + fmt.Sprintf(
211+
"\ninstall --directory --mode=0700 '/tablespaces/%s/data'",
212+
tablespaceVolume.Labels[naming.LabelData])
216213
}
217214

218215
restoreScript := `declare -r pgdata="$1" opts="$2"

internal/pgbackrest/config_test.go

-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import (
3232
"github.com/crunchydata/postgres-operator/internal/initialize"
3333
"github.com/crunchydata/postgres-operator/internal/naming"
3434
"github.com/crunchydata/postgres-operator/internal/testing/require"
35-
"github.com/crunchydata/postgres-operator/internal/util"
3635
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
3736
)
3837

@@ -294,7 +293,6 @@ func TestReloadCommandPrettyYAML(t *testing.T) {
294293
func TestRestoreCommand(t *testing.T) {
295294
shellcheck := require.ShellCheck(t)
296295

297-
assert.NilError(t, util.AddAndSetFeatureGates(string(util.TablespaceVolumes+"=false")))
298296
pgdata := "/pgdata/pg13"
299297
opts := []string{
300298
"--stanza=" + DefaultStanzaName, "--pg1-path=" + pgdata,
@@ -314,7 +312,6 @@ func TestRestoreCommand(t *testing.T) {
314312
}
315313

316314
func TestRestoreCommandPrettyYAML(t *testing.T) {
317-
assert.NilError(t, util.AddAndSetFeatureGates(string(util.TablespaceVolumes+"=false")))
318315
b, err := yaml.Marshal(RestoreCommand("/dir", nil, "--options"))
319316
assert.NilError(t, err)
320317
assert.Assert(t, strings.Contains(string(b), "\n- |"),

internal/pgbackrest/reconcile.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,7 @@ func addServerContainerAndVolume(
307307
for _, instance := range cluster.Spec.InstanceSets {
308308
for _, vol := range instance.TablespaceVolumes {
309309
tablespaceVolumeMount := postgres.TablespaceVolumeMount(vol.Name)
310-
postgresMounts[postgres.TablespaceVolumeMount(vol.Name).Name] = tablespaceVolumeMount
310+
postgresMounts[tablespaceVolumeMount.Name] = tablespaceVolumeMount
311311
}
312312
}
313313
}

internal/pgbackrest/reconcile_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ func TestAddServerToInstancePod(t *testing.T) {
663663
}
664664

665665
out := pod.DeepCopy()
666-
out.Volumes = append(out.Volumes, corev1.Volume{Name: "trial"}, corev1.Volume{Name: "castle"})
666+
out.Volumes = append(out.Volumes, corev1.Volume{Name: "tablespace-trial"}, corev1.Volume{Name: "tablespace-castle"})
667667
AddServerToInstancePod(clusterWithTablespaces, out, "instance-secret-name")
668668

669669
// Only Containers and Volumes fields have changed.
@@ -700,9 +700,9 @@ func TestAddServerToInstancePod(t *testing.T) {
700700
- mountPath: /pgwal
701701
name: postgres-wal
702702
- mountPath: /tablespaces/trial
703-
name: trial
703+
name: tablespace-trial
704704
- mountPath: /tablespaces/castle
705-
name: castle
705+
name: tablespace-castle
706706
- command:
707707
- bash
708708
- -ceu

internal/postgres/config.go

+13-12
Original file line numberDiff line numberDiff line change
@@ -208,26 +208,27 @@ func startupCommand(
208208
// If the user requests tablespaces, we want to make sure the directories exist with the
209209
// correct owner and permissions.
210210
tablespaceCmd := ""
211-
if util.DefaultMutableFeatureGate.Enabled(util.TablespaceVolumes) && instance.TablespaceVolumes != nil {
211+
if util.DefaultMutableFeatureGate.Enabled(util.TablespaceVolumes) {
212212
// This command checks if a dir exists and if not, creates it;
213213
// if the dir does exist, then we `recreate` it to make sure the owner is correct;
214-
// but if the dir exists with the wrong owner and is not writeable, we error.
214+
// if the dir exists with the wrong owner and is not writeable, we error.
215215
// This is the same behavior we use for the main PGDATA directory.
216216
// Note: Postgres requires the tablespace directory to be "an existing, empty directory
217217
// that is owned by the PostgreSQL operating system user."
218218
// - https://www.postgresql.org/docs/current/manage-ag-tablespaces.html
219219
// However, unlike the PGDATA directory, Postgres will _not_ error out
220220
// if the permissions are wrong on the tablespace directory.
221-
// Instead, when a tablespace is created in Postgres, Postgres will
222-
// chmod the tablespace directory to match permissions on the PGDATA directory,
223-
// i.e., 700 or 750
224-
// Postgres setting the directory permissions:
225-
// - https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/backend/commands/tablespace.c;h=5411638696b4d1436a56f62e9cde7ec61797377b;hb=REL_14_0#l604
226-
// Postgres setting directory permissions:
227-
// - https://git.postgresql.org/gitweb/?p=postgresql.git;a=blob;f=src/common/file_perm.c;h=ab85765a0bfb5108265819a4c7e9ff87dc6add10;hb=REL_14_0#l34
228-
// Note: This permission change seems to only happen when the tablespace is created,
229-
// i.e., if a user `chmod`'ed the directory after the creation of the tablespace,
230-
// Postgres would not revert that
221+
// Instead, when a tablespace is created in Postgres, Postgres will `chmod` the
222+
// tablespace directory to match permissions on the PGDATA directory (either 700 or 750).
223+
// Postgres setting the tablespace directory permissions:
224+
// - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/commands/tablespace.c;hb=REL_14_0#l600
225+
// Postgres choosing directory permissions:
226+
// - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/common/file_perm.c;hb=REL_14_0#l27
227+
// Note: This permission change seems to happen only when the tablespace is created in Postgres.
228+
// If the user manually `chmod`'ed the directory after the creation of the tablespace, Postgres
229+
// would not attempt to change the directory permissions.
230+
// Note: as noted below, we mount the tablespace directory to the mountpoint `/tablespaces/NAME`,
231+
// and so we add the subdirectory `data` in order to set the permissions.
231232
checkInstallRecreateCmd := strings.Join([]string{
232233
`if [[ ! -e "${tablespace_dir}" || -O "${tablespace_dir}" ]]; then`,
233234
`install --directory --mode=0700 "${tablespace_dir}"`,

internal/postgres/reconcile.go

+15-17
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func DataVolumeMount() corev1.VolumeMount {
4040

4141
// TablespaceVolumeMount returns the name and mount path of the PostgreSQL tablespace data volume.
4242
func TablespaceVolumeMount(tablespaceName string) corev1.VolumeMount {
43-
return corev1.VolumeMount{Name: tablespaceName, MountPath: tablespaceMountPath + "/" + tablespaceName}
43+
return corev1.VolumeMount{Name: "tablespace-" + tablespaceName, MountPath: tablespaceMountPath + "/" + tablespaceName}
4444
}
4545

4646
// WALVolumeMount returns the name and mount path of the PostgreSQL WAL volume.
@@ -224,25 +224,23 @@ func InstancePod(ctx context.Context,
224224
downwardAPIVolume,
225225
}
226226

227-
// If `TablespaceVolumes` FeatureGate is enabled, add any tablespace volumes to the pod, and
227+
// If `TablespaceVolumes` FeatureGate is enabled, `inTablespaceVolumes` may not be nil.
228+
// In that case, add any tablespace volumes to the pod, and
228229
// add volumeMounts to the database and startup containers
229-
if util.DefaultMutableFeatureGate.Enabled(util.TablespaceVolumes) &&
230-
inTablespaceVolumes != nil {
231-
for _, vol := range inTablespaceVolumes {
232-
tablespaceVolumeMount := TablespaceVolumeMount(vol.Labels["postgres-operator.crunchydata.com/data"])
233-
tablespaceVolume := corev1.Volume{
234-
Name: tablespaceVolumeMount.Name,
235-
VolumeSource: corev1.VolumeSource{
236-
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
237-
ClaimName: vol.Name,
238-
ReadOnly: false,
239-
},
230+
for _, vol := range inTablespaceVolumes {
231+
tablespaceVolumeMount := TablespaceVolumeMount(vol.Labels[naming.LabelData])
232+
tablespaceVolume := corev1.Volume{
233+
Name: tablespaceVolumeMount.Name,
234+
VolumeSource: corev1.VolumeSource{
235+
PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{
236+
ClaimName: vol.Name,
237+
ReadOnly: false,
240238
},
241-
}
242-
outInstancePod.Volumes = append(outInstancePod.Volumes, tablespaceVolume)
243-
container.VolumeMounts = append(container.VolumeMounts, tablespaceVolumeMount)
244-
startup.VolumeMounts = append(startup.VolumeMounts, tablespaceVolumeMount)
239+
},
245240
}
241+
outInstancePod.Volumes = append(outInstancePod.Volumes, tablespaceVolume)
242+
container.VolumeMounts = append(container.VolumeMounts, tablespaceVolumeMount)
243+
startup.VolumeMounts = append(startup.VolumeMounts, tablespaceVolumeMount)
246244
}
247245

248246
if len(inCluster.Spec.Config.Files) != 0 {

internal/postgres/reconcile_test.go

+5-6
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func TestTablespaceVolumeMount(t *testing.T) {
6363
mount := TablespaceVolumeMount("trial")
6464

6565
assert.DeepEqual(t, mount, corev1.VolumeMount{
66-
Name: "trial",
66+
Name: "tablespace-trial",
6767
MountPath: "/tablespaces/trial",
6868
ReadOnly: false,
6969
})
@@ -546,7 +546,6 @@ volumes:
546546
}
547547
tablespaceVolumes := []*corev1.PersistentVolumeClaim{tablespaceVolume1, tablespaceVolume2}
548548

549-
assert.NilError(t, util.AddAndSetFeatureGates(string(util.TablespaceVolumes+"=true")))
550549
InstancePod(ctx, cluster, instance,
551550
serverSecretProjection, clientSecretProjection, dataVolume, nil, tablespaceVolumes, pod)
552551

@@ -560,9 +559,9 @@ volumes:
560559
name: database-containerinfo
561560
readOnly: true
562561
- mountPath: /tablespaces/castle
563-
name: castle
562+
name: tablespace-castle
564563
- mountPath: /tablespaces/trial
565-
name: trial`), "expected tablespace mount(s) in %q container", pod.Containers[0].Name)
564+
name: tablespace-trial`), "expected tablespace mount(s) in %q container", pod.Containers[0].Name)
566565

567566
// InitContainer has all mountPaths, except downwardAPI and additionalConfig
568567
assert.Assert(t, marshalMatches(pod.InitContainers[0].VolumeMounts, `
@@ -572,9 +571,9 @@ volumes:
572571
- mountPath: /pgdata
573572
name: postgres-data
574573
- mountPath: /tablespaces/castle
575-
name: castle
574+
name: tablespace-castle
576575
- mountPath: /tablespaces/trial
577-
name: trial`), "expected tablespace mount(s) in %q container", pod.InitContainers[0].Name)
576+
name: tablespace-trial`), "expected tablespace mount(s) in %q container", pod.InitContainers[0].Name)
578577
})
579578

580579
t.Run("WithWALVolumeWithWALVolumeSpec", func(t *testing.T) {

pkg/apis/postgres-operator.crunchydata.com/v1beta1/postgrescluster_types.go

+12
Original file line numberDiff line numberDiff line change
@@ -481,14 +481,26 @@ type PostgresInstanceSetSpec struct {
481481

482482
// The list of tablespaces volumes to mount for this postgrescluster
483483
// This function is currently in alpha as of PGO v5.4
484+
// +listType=map
485+
// +listMapKey=name
484486
// +optional
485487
TablespaceVolumes []TablespaceVolume `json:"tablespaceVolumes,omitempty"`
486488
}
487489

488490
type TablespaceVolume struct {
491+
// This value goes into
492+
// a. the name of a corev1.PersistentVolumeClaim,
493+
// b. a label value, and
494+
// c. a path name.
495+
// So it must match both IsDNS1123Subdomain and IsValidLabelValue;
496+
// and be valid as a file path.
497+
489498
// The name for the tablespace, used as the path name for the volume.
490499
// Must be unique in the instance set since they become the directory names.
491500
// +kubebuilder:validation:Required
501+
// +kubebuilder:validation:MinLength=1
502+
// +kubebuilder:validation:Pattern=`^[a-z][a-z0-9]*$`
503+
// +kubebuilder:validation:Type=string
492504
Name string `json:"name"`
493505

494506
// Defines a PersistentVolumeClaim for a tablespace.

testing/kuttl/e2e/tablespace-enabled/01--psql-connect.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,5 @@ spec:
4141
CREATE SCHEMA "tablespace-enabled";
4242
CREATE TABLE important (data) TABLESPACE trial AS VALUES ('treasure');
4343
CREATE TABLE also_important (data) TABLESPACE castle AS VALUES ('treasure');
44+
CREATE TABLE moving_important (data) AS VALUES ('treasure');
45+
ALTER TABLE moving_important SET TABLESPACE trial;

0 commit comments

Comments
 (0)