diff --git a/Jenkinsfile b/Jenkinsfile index 4ee37aea83..ba9d97fd10 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -12,7 +12,7 @@ void createCluster(String CLUSTER_SUFFIX) { gcloud auth activate-service-account --key-file $CLIENT_SECRET_FILE gcloud config set project $GCP_PROJECT gcloud container clusters list --filter $CLUSTER_NAME-${CLUSTER_SUFFIX} --zone $region --format='csv[no-heading](name)' | xargs gcloud container clusters delete --zone $region --quiet || true - gcloud container clusters create --zone $region $CLUSTER_NAME-${CLUSTER_SUFFIX} --cluster-version=1.27 --machine-type=n1-standard-4 --preemptible --disk-size 30 --num-nodes=3 --network=jenkins-vpc --subnetwork=jenkins-${CLUSTER_SUFFIX} --no-enable-autoupgrade --cluster-ipv4-cidr=/21 --labels delete-cluster-after-hours=6 --enable-ip-alias --workload-pool=cloud-dev-112233.svc.id.goog && \ + gcloud container clusters create --zone $region $CLUSTER_NAME-${CLUSTER_SUFFIX} --cluster-version=1.28 --machine-type=n1-standard-4 --preemptible --disk-size 30 --num-nodes=3 --network=jenkins-vpc --subnetwork=jenkins-${CLUSTER_SUFFIX} --no-enable-autoupgrade --cluster-ipv4-cidr=/21 --labels delete-cluster-after-hours=6 --enable-ip-alias --workload-pool=cloud-dev-112233.svc.id.goog && \ kubectl create clusterrolebinding cluster-admin-binding --clusterrole cluster-admin --user jenkins@"$GCP_PROJECT".iam.gserviceaccount.com || ret_val=\$? if [ \${ret_val} -eq 0 ]; then break; fi ret_num=\$((ret_num + 1)) diff --git a/Makefile b/Makefile index 977241c079..d21f770da5 100644 --- a/Makefile +++ b/Makefile @@ -422,6 +422,11 @@ release: generate -e "/extensions:/,/image:/{s#image: .*#image: percona/percona-postgresql-operator:$(VERSION)#}" \ -e "/^ pmm:/,/^ image:/{s#image: .*#image: percona/pmm-client:@@SET_TAG@@#}" deploy/cr.yaml sed -i -r "/Version *= \"[0-9]+\.[0-9]+\.[0-9]+\"$$/ s/[0-9]+\.[0-9]+\.[0-9]+/$(VERSION)/" pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go + sed -i \ + -e "/^spec:/,/^ image:/{s#image: .*#image: percona/percona-postgresql-operator:$(VERSION)#}" \ + -e "/^spec:/,/^ toPostgresImage:/{s#toPostgresImage: .*#toPostgresImage: percona/percona-postgresql-operator:$(VERSION)-ppg$(PG_VER)-postgres#}" \ + -e "/^spec:/,/^ toPgBouncerImage:/{s#toPgBouncerImage: .*#toPgBouncerImage: percona/percona-postgresql-operator:$(VERSION)-ppg$(PG_VER)-pgbouncer#}" \ + -e "/^spec:/,/^ toPgBackRestImage:/{s#toPgBackRestImage: .*#toPgBackRestImage: percona/percona-postgresql-operator:$(VERSION)-ppg$(PG_VER)-pgbackres#}" deploy/upgrade.yaml # Prepare main branch after release MAJOR_VER := $(shell grep -oE "crVersion: .*" deploy/cr.yaml|grep -oE "[0-9]+\.[0-9]+\.[0-9]+"|cut -d'.' -f1) @@ -436,3 +441,8 @@ after-release: generate -e "/extensions:/,/image:/{s#image: .*#image: perconalab/percona-postgresql-operator:main#}" \ -e "/^ pmm:/,/^ image:/{s#image: .*#image: perconalab/pmm-client:dev-latest#}" deploy/cr.yaml sed -i -r "/Version *= \"[0-9]+\.[0-9]+\.[0-9]+\"$$/ s/[0-9]+\.[0-9]+\.[0-9]+/$(NEXT_VER)/" pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go + sed -i \ + -e "/^spec:/,/^ image:/{s#image: .*#image: perconalab/percona-postgresql-operator:main#}" \ + -e "/^spec:/,/^ toPostgresImage:/{s#toPostgresImage: .*#toPostgresImage: perconalab/percona-postgresql-operator:main-ppg$(PG_VER)-postgres#}" \ + -e "/^spec:/,/^ toPgBouncerImage:/{s#toPgBouncerImage: .*#toPgBouncerImage: perconalab/percona-postgresql-operator:main-ppg$(PG_VER)-pgbouncer#}" \ + -e "/^spec:/,/^ toPgBackRestImage:/{s#toPgBackRestImage: .*#toPgBackRestImage: perconalab/percona-postgresql-operator:main-ppg$(PG_VER)-pgbackrest#}" deploy/upgrade.yaml diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 704248d479..a48f23888e 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -16,5 +16,5 @@ resources: images: - name: percona-postgresql-operator - newName: perconalab/percona-postgresql-operator - newTag: main + newName: percona/percona-postgresql-operator + newTag: 2.5.0 diff --git a/deploy/cr.yaml b/deploy/cr.yaml index ee2968970d..47c629bb48 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -6,7 +6,7 @@ metadata: # - percona.com/delete-pvc # - percona.com/delete-ssl spec: - crVersion: 2.5.0 + crVersion: 2.6.0 # secrets: # customRootCATLSSecret: # name: cluster1-ca-cert @@ -46,6 +46,7 @@ spec: # dataSource: # postgresCluster: # clusterName: cluster1 +# clusterNamespace: cluster1-namespace # repoName: repo1 # options: # - --type=time @@ -214,6 +215,8 @@ spec: resources: requests: storage: 1Gi +# limits: +# storage: 5Gi # tablespaceVolumes: # - name: user # dataVolumeClaimSpec: diff --git a/deploy/upgrade.yaml b/deploy/upgrade.yaml index f0f03026c9..8b93ccc707 100644 --- a/deploy/upgrade.yaml +++ b/deploy/upgrade.yaml @@ -4,7 +4,7 @@ metadata: name: cluster1-15-to-16 spec: postgresClusterName: cluster1 - image: perconalab/percona-postgresql-operator:main-upgrade + image: perconalab/percona-postgresql-operator:main fromPostgresVersion: 15 toPostgresVersion: 16 toPostgresImage: perconalab/percona-postgresql-operator:main-ppg16-postgres diff --git a/e2e-tests/functions b/e2e-tests/functions index 3f9ceca9ee..0c16637126 100644 --- a/e2e-tests/functions +++ b/e2e-tests/functions @@ -2,7 +2,7 @@ # set root repo relatively to a test dir ROOT_REPO=${ROOT_REPO:-$(realpath ../../..)} -CERT_MANAGER_VER="1.14.2" +CERT_MANAGER_VER="1.15.3" test_name=$(basename "$(pwd)") source "${ROOT_REPO}/e2e-tests/vars.sh" @@ -58,6 +58,25 @@ deploy_operator() { | kubectl -n "${OPERATOR_NS:-$NAMESPACE}" apply -f - } +update_operator() { + local cw_prefix="" + + if [[ $OPERATOR_NS ]]; then + cw_prefix="cw-" + fi + + kubectl -n "${OPERATOR_NS:-$NAMESPACE}" apply --server-side --force-conflicts -f "${DEPLOY_DIR}/crd.yaml" + kubectl -n "${OPERATOR_NS:-$NAMESPACE}" apply --server-side --force-conflicts -f "${DEPLOY_DIR}/${cw_prefix}rbac.yaml" + + local disable_telemetry=true + if [ "${test_name}" == "telemetry-transfer" ]; then + disable_telemetry=false + fi + + kubectl -n "${OPERATOR_NS:-$NAMESPACE}" patch deployment percona-postgresql-operator -p \ + '{"spec":{"template":{"spec":{"containers":[{"name":"operator","image":"'${IMAGE}'"}]}}}}' +} + deploy_operator_gh() { local git_tag="$1" local cw_prefix="" @@ -740,3 +759,17 @@ get_container_image() { echo "${IMAGE_BASE}:${operatorVersion}-ppg${pgVersion}-${component}" } + +get_postgresql_logs() { + local pgVersion=$1 + + for pod in $(kubectl get pods -l postgres-operator.crunchydata.com/data=postgres --no-headers | awk '{print $1}'); do + local phase=$(kubectl -n ${NAMESPACE} get ${pod} -o jsonpath={".status.phase"}) + if [[ "${phase}" != "Running" ]]; then + echo "Waiting for ${pod} to start running" + continue + fi + echo "find /pgdata/pg${pgVersion}/log -type f -iname 'postgresql*.log' -exec tail -n 30 {} \;" \ + | kubectl -n ${NAMESPACE} exec -it ${pod} -- bash 2>/dev/null + done +} diff --git a/e2e-tests/tests/custom-extensions/02-assert.yaml b/e2e-tests/tests/custom-extensions/02-assert.yaml index ab3625605a..9ab91fde3e 100644 --- a/e2e-tests/tests/custom-extensions/02-assert.yaml +++ b/e2e-tests/tests/custom-extensions/02-assert.yaml @@ -22,7 +22,6 @@ spec: - name: postgres-startup - command: - /usr/local/bin/relocate-extensions.sh - name: extension-relocator-16 resources: {} volumeMounts: - mountPath: /pgdata @@ -45,7 +44,6 @@ spec: envFrom: - secretRef: name: aws-s3-secret - name: extension-installer-16 resources: {} volumeMounts: - mountPath: /pgdata diff --git a/e2e-tests/tests/custom-extensions/05-assert.yaml b/e2e-tests/tests/custom-extensions/05-assert.yaml index ab3625605a..9ab91fde3e 100644 --- a/e2e-tests/tests/custom-extensions/05-assert.yaml +++ b/e2e-tests/tests/custom-extensions/05-assert.yaml @@ -22,7 +22,6 @@ spec: - name: postgres-startup - command: - /usr/local/bin/relocate-extensions.sh - name: extension-relocator-16 resources: {} volumeMounts: - mountPath: /pgdata @@ -45,7 +44,6 @@ spec: envFrom: - secretRef: name: aws-s3-secret - name: extension-installer-16 resources: {} volumeMounts: - mountPath: /pgdata diff --git a/e2e-tests/tests/custom-extensions/06-assert.yaml b/e2e-tests/tests/custom-extensions/06-assert.yaml index bef145ff6e..f69b1eff1b 100644 --- a/e2e-tests/tests/custom-extensions/06-assert.yaml +++ b/e2e-tests/tests/custom-extensions/06-assert.yaml @@ -24,7 +24,6 @@ spec: - /usr/local/bin/relocate-extensions.sh env: - name: PG_VERSION - name: extension-relocator-16 resources: {} volumeMounts: - mountPath: /pgdata @@ -47,7 +46,6 @@ spec: envFrom: - secretRef: name: aws-s3-secret - name: extension-installer-16 resources: {} volumeMounts: - mountPath: /pgdata diff --git a/e2e-tests/tests/custom-extensions/07-assert.yaml b/e2e-tests/tests/custom-extensions/07-assert.yaml index 904c3290fb..6e8e4b38cb 100644 --- a/e2e-tests/tests/custom-extensions/07-assert.yaml +++ b/e2e-tests/tests/custom-extensions/07-assert.yaml @@ -20,8 +20,10 @@ spec: spec: initContainers: - name: postgres-startup - - name: extension-relocator-16 - - name: extension-installer-16 + - command: + - /usr/local/bin/relocate-extensions.sh + - command: + - /usr/local/bin/install-extensions.sh - name: nss-wrapper-init status: observedGeneration: 3 diff --git a/e2e-tests/tests/major-upgrade/01-create-cluster.yaml b/e2e-tests/tests/major-upgrade/01-create-cluster.yaml index 1380fae4aa..729fbcc51f 100644 --- a/e2e-tests/tests/major-upgrade/01-create-cluster.yaml +++ b/e2e-tests/tests/major-upgrade/01-create-cluster.yaml @@ -7,7 +7,7 @@ commands: set -o xtrace source ../../functions - + get_cr \ | yq eval ' .spec.postgresVersion = 12 | diff --git a/e2e-tests/tests/major-upgrade/10-assert.yaml b/e2e-tests/tests/major-upgrade/10-assert.yaml index 66d99bc294..ae8dcb18f8 100644 --- a/e2e-tests/tests/major-upgrade/10-assert.yaml +++ b/e2e-tests/tests/major-upgrade/10-assert.yaml @@ -1,6 +1,10 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 360 +commands: +- script: |- + kubectl -n ${NAMESPACE} get pg,pod,job + sleep 5 --- apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster @@ -20,3 +24,19 @@ status: ready: 3 size: 3 state: ready +--- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: major-upgrade + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-backup: replica-create + postgres-operator.crunchydata.com/pgbackrest-repo: repo1 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 diff --git a/e2e-tests/tests/major-upgrade/11-assert.yaml b/e2e-tests/tests/major-upgrade/11-assert.yaml index 525d522912..47ccba03b5 100644 --- a/e2e-tests/tests/major-upgrade/11-assert.yaml +++ b/e2e-tests/tests/major-upgrade/11-assert.yaml @@ -1,31 +1,21 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 660 +commands: +- script: |- + kubectl get postgrescluster major-upgrade \ + -n ${NAMESPACE} \ + -o yaml \ + | yq eval '.status.pgbackrest.repos' - --- -kind: Job -apiVersion: batch/v1 +apiVersion: postgres-operator.crunchydata.com/v1beta1 +kind: PostgresCluster metadata: - annotations: - postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-12-to-13 - labels: - postgres-operator.crunchydata.com/pgbackrest-backup: manual - postgres-operator.crunchydata.com/pgbackrest-repo: repo1 - ownerReferences: - - apiVersion: pgv2.percona.com/v2 - kind: PerconaPGBackup - controller: true - blockOwnerDeletion: true + name: major-upgrade status: - succeeded: 1 ---- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGBackup -metadata: - name: backup-after-12-to-13 -spec: - pgCluster: major-upgrade - repoName: repo1 - options: - - --type=full -status: - state: Succeeded + pgbackrest: + repos: + - bound: true + name: repo2 + replicaCreateBackupComplete: true + stanzaCreated: true diff --git a/e2e-tests/tests/major-upgrade/11-change-repo.yaml b/e2e-tests/tests/major-upgrade/11-change-repo.yaml new file mode 100644 index 0000000000..22094b19f4 --- /dev/null +++ b/e2e-tests/tests/major-upgrade/11-change-repo.yaml @@ -0,0 +1,12 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: |- + set -o errexit + set -o xtrace + + kubectl patch \ + -n $NAMESPACE \ + perconapgcluster major-upgrade \ + --type='json' \ + -p='[{"op": "replace", "path": "/spec/backups/pgbackrest/repos/0/name", "value": "repo2"}]' diff --git a/e2e-tests/tests/major-upgrade/12-assert.yaml b/e2e-tests/tests/major-upgrade/12-assert.yaml index cb8a90c13f..d1790eabf5 100644 --- a/e2e-tests/tests/major-upgrade/12-assert.yaml +++ b/e2e-tests/tests/major-upgrade/12-assert.yaml @@ -1,30 +1,31 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 720 +timeout: 660 --- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGRestore +kind: Job +apiVersion: batch/v1 metadata: - name: restore-after-12-to-13 -spec: - pgCluster: major-upgrade - repoName: repo1 + annotations: + postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-12-to-13 + labels: + postgres-operator.crunchydata.com/pgbackrest-backup: manual + postgres-operator.crunchydata.com/pgbackrest-repo: repo2 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true status: - state: Succeeded + succeeded: 1 --- apiVersion: pgv2.percona.com/v2 -kind: PerconaPGCluster +kind: PerconaPGBackup metadata: - name: major-upgrade + name: backup-after-12-to-13 +spec: + pgCluster: major-upgrade + repoName: repo2 + options: + - --type=full status: - pgbouncer: - ready: 3 - size: 3 - postgres: - instances: - - name: instance1 - ready: 3 - size: 3 - ready: 3 - size: 3 - state: ready + state: Succeeded diff --git a/e2e-tests/tests/major-upgrade/11-run-backup.yaml b/e2e-tests/tests/major-upgrade/12-run-backup.yaml similarity index 89% rename from e2e-tests/tests/major-upgrade/11-run-backup.yaml rename to e2e-tests/tests/major-upgrade/12-run-backup.yaml index 0cfac94a95..601008d4ca 100644 --- a/e2e-tests/tests/major-upgrade/11-run-backup.yaml +++ b/e2e-tests/tests/major-upgrade/12-run-backup.yaml @@ -4,6 +4,6 @@ metadata: name: backup-after-12-to-13 spec: pgCluster: major-upgrade - repoName: repo1 + repoName: repo2 options: - --type=full diff --git a/e2e-tests/tests/major-upgrade/13-assert.yaml b/e2e-tests/tests/major-upgrade/13-assert.yaml index 7c65443fbe..2d05bb7d88 100644 --- a/e2e-tests/tests/major-upgrade/13-assert.yaml +++ b/e2e-tests/tests/major-upgrade/13-assert.yaml @@ -1,10 +1,52 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 30 +timeout: 720 +commands: +- script: |- + set -o errexit + + kubectl -n ${NAMESPACE} get pod + + for pod in $(kubectl -n ${NAMESPACE} get pods -l postgres-operator.crunchydata.com/data=postgres --no-headers | awk '{print $1}'); do + phase=$(kubectl -n ${NAMESPACE} get pod/${pod} -o jsonpath={".status.phase"}) + if [[ "${phase}" != "Running" ]]; then + echo "Waiting for ${pod} to start running" + continue + fi + echo "PostgreSQL logs from ${pod}:" + echo "find /pgdata/pg13/log -type f -iname 'postgresql*.log' -exec tail -n 30 {} \;" \ + | kubectl -n ${NAMESPACE} exec -it ${pod} -- bash 2>/dev/null + done + + sleep 30 +collectors: +- type: pod + selector: "postgres-operator.crunchydata.com/data=postgres" + tail: 30 --- -kind: ConfigMap -apiVersion: v1 +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGRestore metadata: - name: 05-read-from-primary -data: - data: ' 100500' + name: restore-after-12-to-13 +spec: + pgCluster: major-upgrade + repoName: repo2 +status: + state: Succeeded +--- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + name: major-upgrade +status: + pgbouncer: + ready: 3 + size: 3 + postgres: + instances: + - name: instance1 + ready: 3 + size: 3 + ready: 3 + size: 3 + state: ready diff --git a/e2e-tests/tests/major-upgrade/12-run-restore.yaml b/e2e-tests/tests/major-upgrade/13-run-restore.yaml similarity index 87% rename from e2e-tests/tests/major-upgrade/12-run-restore.yaml rename to e2e-tests/tests/major-upgrade/13-run-restore.yaml index 6350f5d6f1..bba96fe985 100644 --- a/e2e-tests/tests/major-upgrade/12-run-restore.yaml +++ b/e2e-tests/tests/major-upgrade/13-run-restore.yaml @@ -4,4 +4,4 @@ metadata: name: restore-after-12-to-13 spec: pgCluster: major-upgrade - repoName: repo1 + repoName: repo2 diff --git a/e2e-tests/tests/major-upgrade/14-assert.yaml b/e2e-tests/tests/major-upgrade/14-assert.yaml new file mode 100644 index 0000000000..7c65443fbe --- /dev/null +++ b/e2e-tests/tests/major-upgrade/14-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: 05-read-from-primary +data: + data: ' 100500' diff --git a/e2e-tests/tests/major-upgrade/13-read-from-primary.yaml b/e2e-tests/tests/major-upgrade/14-read-from-primary.yaml similarity index 100% rename from e2e-tests/tests/major-upgrade/13-read-from-primary.yaml rename to e2e-tests/tests/major-upgrade/14-read-from-primary.yaml diff --git a/e2e-tests/tests/major-upgrade/20-assert.yaml b/e2e-tests/tests/major-upgrade/20-assert.yaml index d60a84442f..c506a745f5 100644 --- a/e2e-tests/tests/major-upgrade/20-assert.yaml +++ b/e2e-tests/tests/major-upgrade/20-assert.yaml @@ -1,6 +1,10 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 360 +commands: +- script: |- + kubectl -n ${NAMESPACE} get pg,pod,job + sleep 5 --- apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster @@ -20,3 +24,19 @@ status: ready: 3 size: 3 state: ready +--- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: major-upgrade + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-backup: replica-create + postgres-operator.crunchydata.com/pgbackrest-repo: repo2 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 diff --git a/e2e-tests/tests/major-upgrade/21-assert.yaml b/e2e-tests/tests/major-upgrade/21-assert.yaml index 788d3de044..301240b0f5 100644 --- a/e2e-tests/tests/major-upgrade/21-assert.yaml +++ b/e2e-tests/tests/major-upgrade/21-assert.yaml @@ -1,31 +1,21 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 660 +commands: +- script: |- + kubectl get postgrescluster major-upgrade \ + -n ${NAMESPACE} \ + -o yaml \ + | yq eval '.status.pgbackrest.repos' - --- -kind: Job -apiVersion: batch/v1 +apiVersion: postgres-operator.crunchydata.com/v1beta1 +kind: PostgresCluster metadata: - annotations: - postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-13-to-14 - labels: - postgres-operator.crunchydata.com/pgbackrest-backup: manual - postgres-operator.crunchydata.com/pgbackrest-repo: repo1 - ownerReferences: - - apiVersion: pgv2.percona.com/v2 - kind: PerconaPGBackup - controller: true - blockOwnerDeletion: true + name: major-upgrade status: - succeeded: 1 ---- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGBackup -metadata: - name: backup-after-13-to-14 -spec: - pgCluster: major-upgrade - repoName: repo1 - options: - - --type=full -status: - state: Succeeded + pgbackrest: + repos: + - bound: true + name: repo3 + replicaCreateBackupComplete: true + stanzaCreated: true diff --git a/e2e-tests/tests/major-upgrade/21-change-repo.yaml b/e2e-tests/tests/major-upgrade/21-change-repo.yaml new file mode 100644 index 0000000000..57f9edc8a4 --- /dev/null +++ b/e2e-tests/tests/major-upgrade/21-change-repo.yaml @@ -0,0 +1,12 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: |- + set -o errexit + set -o xtrace + + kubectl patch \ + -n $NAMESPACE \ + perconapgcluster major-upgrade \ + --type='json' \ + -p='[{"op": "replace", "path": "/spec/backups/pgbackrest/repos/0/name", "value": "repo3"}]' diff --git a/e2e-tests/tests/major-upgrade/22-assert.yaml b/e2e-tests/tests/major-upgrade/22-assert.yaml index 2b8c164f6e..7a2f6c1c0f 100644 --- a/e2e-tests/tests/major-upgrade/22-assert.yaml +++ b/e2e-tests/tests/major-upgrade/22-assert.yaml @@ -1,30 +1,31 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 720 +timeout: 660 --- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGRestore +kind: Job +apiVersion: batch/v1 metadata: - name: restore-after-13-to-14 -spec: - pgCluster: major-upgrade - repoName: repo1 + annotations: + postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-13-to-14 + labels: + postgres-operator.crunchydata.com/pgbackrest-backup: manual + postgres-operator.crunchydata.com/pgbackrest-repo: repo3 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true status: - state: Succeeded + succeeded: 1 --- apiVersion: pgv2.percona.com/v2 -kind: PerconaPGCluster +kind: PerconaPGBackup metadata: - name: major-upgrade + name: backup-after-13-to-14 +spec: + pgCluster: major-upgrade + repoName: repo3 + options: + - --type=full status: - pgbouncer: - ready: 3 - size: 3 - postgres: - instances: - - name: instance1 - ready: 3 - size: 3 - ready: 3 - size: 3 - state: ready + state: Succeeded diff --git a/e2e-tests/tests/major-upgrade/21-run-backup.yaml b/e2e-tests/tests/major-upgrade/22-run-backup.yaml similarity index 89% rename from e2e-tests/tests/major-upgrade/21-run-backup.yaml rename to e2e-tests/tests/major-upgrade/22-run-backup.yaml index 7f60f47386..a7a1050c53 100644 --- a/e2e-tests/tests/major-upgrade/21-run-backup.yaml +++ b/e2e-tests/tests/major-upgrade/22-run-backup.yaml @@ -4,6 +4,6 @@ metadata: name: backup-after-13-to-14 spec: pgCluster: major-upgrade - repoName: repo1 + repoName: repo3 options: - --type=full diff --git a/e2e-tests/tests/major-upgrade/23-assert.yaml b/e2e-tests/tests/major-upgrade/23-assert.yaml index c86dbb55fc..75b542e689 100644 --- a/e2e-tests/tests/major-upgrade/23-assert.yaml +++ b/e2e-tests/tests/major-upgrade/23-assert.yaml @@ -1,10 +1,52 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 30 +timeout: 720 +commands: +- script: |- + set -o errexit + + kubectl -n ${NAMESPACE} get pod + + for pod in $(kubectl -n ${NAMESPACE} get pods -l postgres-operator.crunchydata.com/data=postgres --no-headers | awk '{print $1}'); do + phase=$(kubectl -n ${NAMESPACE} get pod/${pod} -o jsonpath={".status.phase"}) + if [[ "${phase}" != "Running" ]]; then + echo "Waiting for ${pod} to start running" + continue + fi + echo "PostgreSQL logs from ${pod}:" + echo "find /pgdata/pg14/log -type f -iname 'postgresql*.log' -exec tail -n 30 {} \;" \ + | kubectl -n ${NAMESPACE} exec -it ${pod} -- bash 2>/dev/null + done + + sleep 30 +collectors: +- type: pod + selector: "postgres-operator.crunchydata.com/data=postgres" + tail: 30 --- -kind: ConfigMap -apiVersion: v1 +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGRestore metadata: - name: 07-read-from-primary -data: - data: ' 100500' + name: restore-after-13-to-14 +spec: + pgCluster: major-upgrade + repoName: repo3 +status: + state: Succeeded +--- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + name: major-upgrade +status: + pgbouncer: + ready: 3 + size: 3 + postgres: + instances: + - name: instance1 + ready: 3 + size: 3 + ready: 3 + size: 3 + state: ready diff --git a/e2e-tests/tests/major-upgrade/22-run-restore.yaml b/e2e-tests/tests/major-upgrade/23-run-restore.yaml similarity index 87% rename from e2e-tests/tests/major-upgrade/22-run-restore.yaml rename to e2e-tests/tests/major-upgrade/23-run-restore.yaml index 7ce7fed535..079ebf7b77 100644 --- a/e2e-tests/tests/major-upgrade/22-run-restore.yaml +++ b/e2e-tests/tests/major-upgrade/23-run-restore.yaml @@ -4,4 +4,4 @@ metadata: name: restore-after-13-to-14 spec: pgCluster: major-upgrade - repoName: repo1 + repoName: repo3 diff --git a/e2e-tests/tests/major-upgrade/24-assert.yaml b/e2e-tests/tests/major-upgrade/24-assert.yaml new file mode 100644 index 0000000000..c86dbb55fc --- /dev/null +++ b/e2e-tests/tests/major-upgrade/24-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: 07-read-from-primary +data: + data: ' 100500' diff --git a/e2e-tests/tests/major-upgrade/23-read-from-primary.yaml b/e2e-tests/tests/major-upgrade/24-read-from-primary.yaml similarity index 100% rename from e2e-tests/tests/major-upgrade/23-read-from-primary.yaml rename to e2e-tests/tests/major-upgrade/24-read-from-primary.yaml diff --git a/e2e-tests/tests/major-upgrade/30-assert.yaml b/e2e-tests/tests/major-upgrade/30-assert.yaml index 1d058344ee..b871b4faf8 100644 --- a/e2e-tests/tests/major-upgrade/30-assert.yaml +++ b/e2e-tests/tests/major-upgrade/30-assert.yaml @@ -1,6 +1,10 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 360 +commands: +- script: |- + kubectl -n ${NAMESPACE} get pg,pod,job + sleep 5 --- apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster @@ -20,3 +24,19 @@ status: ready: 3 size: 3 state: ready +--- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: major-upgrade + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-backup: replica-create + postgres-operator.crunchydata.com/pgbackrest-repo: repo3 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 diff --git a/e2e-tests/tests/major-upgrade/31-assert.yaml b/e2e-tests/tests/major-upgrade/31-assert.yaml index 14ca1327c2..6af985e4b7 100644 --- a/e2e-tests/tests/major-upgrade/31-assert.yaml +++ b/e2e-tests/tests/major-upgrade/31-assert.yaml @@ -1,31 +1,21 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 660 +commands: +- script: |- + kubectl get postgrescluster major-upgrade \ + -n ${NAMESPACE} \ + -o yaml \ + | yq eval '.status.pgbackrest.repos' - --- -kind: Job -apiVersion: batch/v1 +apiVersion: postgres-operator.crunchydata.com/v1beta1 +kind: PostgresCluster metadata: - annotations: - postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-14-to-15 - labels: - postgres-operator.crunchydata.com/pgbackrest-backup: manual - postgres-operator.crunchydata.com/pgbackrest-repo: repo1 - ownerReferences: - - apiVersion: pgv2.percona.com/v2 - kind: PerconaPGBackup - controller: true - blockOwnerDeletion: true + name: major-upgrade status: - succeeded: 1 ---- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGBackup -metadata: - name: backup-after-14-to-15 -spec: - pgCluster: major-upgrade - repoName: repo1 - options: - - --type=full -status: - state: Succeeded + pgbackrest: + repos: + - bound: true + name: repo4 + replicaCreateBackupComplete: true + stanzaCreated: true diff --git a/e2e-tests/tests/major-upgrade/31-change-repo.yaml b/e2e-tests/tests/major-upgrade/31-change-repo.yaml new file mode 100644 index 0000000000..33711377c4 --- /dev/null +++ b/e2e-tests/tests/major-upgrade/31-change-repo.yaml @@ -0,0 +1,12 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: |- + set -o errexit + set -o xtrace + + kubectl patch \ + -n $NAMESPACE \ + perconapgcluster major-upgrade \ + --type='json' \ + -p='[{"op": "replace", "path": "/spec/backups/pgbackrest/repos/0/name", "value": "repo4"}]' diff --git a/e2e-tests/tests/major-upgrade/32-assert.yaml b/e2e-tests/tests/major-upgrade/32-assert.yaml index 0246ec0e79..75daf8364c 100644 --- a/e2e-tests/tests/major-upgrade/32-assert.yaml +++ b/e2e-tests/tests/major-upgrade/32-assert.yaml @@ -1,30 +1,31 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 720 +timeout: 660 --- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGRestore +kind: Job +apiVersion: batch/v1 metadata: - name: restore-after-14-to-15 -spec: - pgCluster: major-upgrade - repoName: repo1 + annotations: + postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-14-to-15 + labels: + postgres-operator.crunchydata.com/pgbackrest-backup: manual + postgres-operator.crunchydata.com/pgbackrest-repo: repo4 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true status: - state: Succeeded + succeeded: 1 --- apiVersion: pgv2.percona.com/v2 -kind: PerconaPGCluster +kind: PerconaPGBackup metadata: - name: major-upgrade + name: backup-after-14-to-15 +spec: + pgCluster: major-upgrade + repoName: repo4 + options: + - --type=full status: - pgbouncer: - ready: 3 - size: 3 - postgres: - instances: - - name: instance1 - ready: 3 - size: 3 - ready: 3 - size: 3 - state: ready + state: Succeeded diff --git a/e2e-tests/tests/major-upgrade/31-run-backup.yaml b/e2e-tests/tests/major-upgrade/32-run-backup.yaml similarity index 89% rename from e2e-tests/tests/major-upgrade/31-run-backup.yaml rename to e2e-tests/tests/major-upgrade/32-run-backup.yaml index e787fedfca..792209c20c 100644 --- a/e2e-tests/tests/major-upgrade/31-run-backup.yaml +++ b/e2e-tests/tests/major-upgrade/32-run-backup.yaml @@ -4,6 +4,6 @@ metadata: name: backup-after-14-to-15 spec: pgCluster: major-upgrade - repoName: repo1 + repoName: repo4 options: - --type=full diff --git a/e2e-tests/tests/major-upgrade/33-assert.yaml b/e2e-tests/tests/major-upgrade/33-assert.yaml index 7321c61abe..1d3a8a9aae 100644 --- a/e2e-tests/tests/major-upgrade/33-assert.yaml +++ b/e2e-tests/tests/major-upgrade/33-assert.yaml @@ -1,10 +1,52 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 30 +timeout: 720 +commands: +- script: |- + set -o errexit + + kubectl -n ${NAMESPACE} get pod + + for pod in $(kubectl -n ${NAMESPACE} get pods -l postgres-operator.crunchydata.com/data=postgres --no-headers | awk '{print $1}'); do + phase=$(kubectl -n ${NAMESPACE} get pod/${pod} -o jsonpath={".status.phase"}) + if [[ "${phase}" != "Running" ]]; then + echo "Waiting for ${pod} to start running" + continue + fi + echo "PostgreSQL logs from ${pod}:" + echo "find /pgdata/pg15/log -type f -iname 'postgresql*.log' -exec tail -n 30 {} \;" \ + | kubectl -n ${NAMESPACE} exec -it ${pod} -- bash 2>/dev/null + done + + sleep 30 +collectors: +- type: pod + selector: "postgres-operator.crunchydata.com/data=postgres" + tail: 30 --- -kind: ConfigMap -apiVersion: v1 +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGRestore metadata: - name: 09-read-from-primary -data: - data: ' 100500' + name: restore-after-14-to-15 +spec: + pgCluster: major-upgrade + repoName: repo4 +status: + state: Succeeded +--- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + name: major-upgrade +status: + pgbouncer: + ready: 3 + size: 3 + postgres: + instances: + - name: instance1 + ready: 3 + size: 3 + ready: 3 + size: 3 + state: ready diff --git a/e2e-tests/tests/major-upgrade/32-run-restore.yaml b/e2e-tests/tests/major-upgrade/33-run-restore.yaml similarity index 87% rename from e2e-tests/tests/major-upgrade/32-run-restore.yaml rename to e2e-tests/tests/major-upgrade/33-run-restore.yaml index 24845cbb29..8e7c7c6dab 100644 --- a/e2e-tests/tests/major-upgrade/32-run-restore.yaml +++ b/e2e-tests/tests/major-upgrade/33-run-restore.yaml @@ -4,5 +4,5 @@ metadata: name: restore-after-14-to-15 spec: pgCluster: major-upgrade - repoName: repo1 + repoName: repo4 diff --git a/e2e-tests/tests/major-upgrade/34-assert.yaml b/e2e-tests/tests/major-upgrade/34-assert.yaml new file mode 100644 index 0000000000..7321c61abe --- /dev/null +++ b/e2e-tests/tests/major-upgrade/34-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: 09-read-from-primary +data: + data: ' 100500' diff --git a/e2e-tests/tests/major-upgrade/33-read-from-primary.yaml b/e2e-tests/tests/major-upgrade/34-read-from-primary.yaml similarity index 100% rename from e2e-tests/tests/major-upgrade/33-read-from-primary.yaml rename to e2e-tests/tests/major-upgrade/34-read-from-primary.yaml diff --git a/e2e-tests/tests/major-upgrade/40-assert.yaml b/e2e-tests/tests/major-upgrade/40-assert.yaml index 58f19f4e69..de3a338c83 100644 --- a/e2e-tests/tests/major-upgrade/40-assert.yaml +++ b/e2e-tests/tests/major-upgrade/40-assert.yaml @@ -1,6 +1,10 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 360 +commands: +- script: |- + kubectl -n ${NAMESPACE} get pg,pod,job + sleep 5 --- apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster @@ -20,3 +24,19 @@ status: ready: 3 size: 3 state: ready +--- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: major-upgrade + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-backup: replica-create + postgres-operator.crunchydata.com/pgbackrest-repo: repo4 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 diff --git a/e2e-tests/tests/major-upgrade/41-assert.yaml b/e2e-tests/tests/major-upgrade/41-assert.yaml index 2b003b69a9..1b95780c8f 100644 --- a/e2e-tests/tests/major-upgrade/41-assert.yaml +++ b/e2e-tests/tests/major-upgrade/41-assert.yaml @@ -1,31 +1,21 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 660 +commands: +- script: |- + kubectl get postgrescluster major-upgrade \ + -n ${NAMESPACE} \ + -o yaml \ + | yq eval '.status.pgbackrest.repos' - --- -kind: Job -apiVersion: batch/v1 +apiVersion: postgres-operator.crunchydata.com/v1beta1 +kind: PostgresCluster metadata: - annotations: - postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-15-to-16 - labels: - postgres-operator.crunchydata.com/pgbackrest-backup: manual - postgres-operator.crunchydata.com/pgbackrest-repo: repo1 - ownerReferences: - - apiVersion: pgv2.percona.com/v2 - kind: PerconaPGBackup - controller: true - blockOwnerDeletion: true + name: major-upgrade status: - succeeded: 1 ---- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGBackup -metadata: - name: backup-after-15-to-16 -spec: - pgCluster: major-upgrade - repoName: repo1 - options: - - --type=full -status: - state: Succeeded + pgbackrest: + repos: + - bound: true + name: repo1 + replicaCreateBackupComplete: true + stanzaCreated: true diff --git a/e2e-tests/tests/major-upgrade/41-change-repo.yaml b/e2e-tests/tests/major-upgrade/41-change-repo.yaml new file mode 100644 index 0000000000..d3d277599f --- /dev/null +++ b/e2e-tests/tests/major-upgrade/41-change-repo.yaml @@ -0,0 +1,12 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: |- + set -o errexit + set -o xtrace + + kubectl patch \ + -n $NAMESPACE \ + perconapgcluster major-upgrade \ + --type='json' \ + -p='[{"op": "replace", "path": "/spec/backups/pgbackrest/repos/0/name", "value": "repo1"}]' diff --git a/e2e-tests/tests/major-upgrade/42-assert.yaml b/e2e-tests/tests/major-upgrade/42-assert.yaml index ff07785690..2b003b69a9 100644 --- a/e2e-tests/tests/major-upgrade/42-assert.yaml +++ b/e2e-tests/tests/major-upgrade/42-assert.yaml @@ -1,30 +1,31 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 720 +timeout: 660 +--- +kind: Job +apiVersion: batch/v1 +metadata: + annotations: + postgres-operator.crunchydata.com/pgbackrest-backup: backup-after-15-to-16 + labels: + postgres-operator.crunchydata.com/pgbackrest-backup: manual + postgres-operator.crunchydata.com/pgbackrest-repo: repo1 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 --- apiVersion: pgv2.percona.com/v2 -kind: PerconaPGRestore +kind: PerconaPGBackup metadata: - name: restore-after-15-to-16 + name: backup-after-15-to-16 spec: pgCluster: major-upgrade repoName: repo1 + options: + - --type=full status: state: Succeeded ---- -apiVersion: pgv2.percona.com/v2 -kind: PerconaPGCluster -metadata: - name: major-upgrade -status: - pgbouncer: - ready: 3 - size: 3 - postgres: - instances: - - name: instance1 - ready: 3 - size: 3 - ready: 3 - size: 3 - state: ready diff --git a/e2e-tests/tests/major-upgrade/41-run-backup.yaml b/e2e-tests/tests/major-upgrade/42-run-backup.yaml similarity index 100% rename from e2e-tests/tests/major-upgrade/41-run-backup.yaml rename to e2e-tests/tests/major-upgrade/42-run-backup.yaml diff --git a/e2e-tests/tests/major-upgrade/43-assert.yaml b/e2e-tests/tests/major-upgrade/43-assert.yaml index 95d57c293f..3114c23118 100644 --- a/e2e-tests/tests/major-upgrade/43-assert.yaml +++ b/e2e-tests/tests/major-upgrade/43-assert.yaml @@ -1,10 +1,52 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 30 +timeout: 720 +commands: +- script: |- + set -o errexit + + kubectl -n ${NAMESPACE} get pod + + for pod in $(kubectl -n ${NAMESPACE} get pods -l postgres-operator.crunchydata.com/data=postgres --no-headers | awk '{print $1}'); do + phase=$(kubectl -n ${NAMESPACE} get pod/${pod} -o jsonpath={".status.phase"}) + if [[ "${phase}" != "Running" ]]; then + echo "Waiting for ${pod} to start running" + continue + fi + echo "PostgreSQL logs from ${pod}:" + echo "find /pgdata/pg16/log -type f -iname 'postgresql*.log' -exec tail -n 30 {} \;" \ + | kubectl -n ${NAMESPACE} exec -it ${pod} -- bash 2>/dev/null + done + + sleep 30 +collectors: +- type: pod + selector: "postgres-operator.crunchydata.com/data=postgres" + tail: 30 --- -kind: ConfigMap -apiVersion: v1 +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGRestore metadata: - name: 11-read-from-primary -data: - data: ' 100500' + name: restore-after-15-to-16 +spec: + pgCluster: major-upgrade + repoName: repo1 +status: + state: Succeeded +--- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + name: major-upgrade +status: + pgbouncer: + ready: 3 + size: 3 + postgres: + instances: + - name: instance1 + ready: 3 + size: 3 + ready: 3 + size: 3 + state: ready diff --git a/e2e-tests/tests/major-upgrade/42-run-restore.yaml b/e2e-tests/tests/major-upgrade/43-run-restore.yaml similarity index 100% rename from e2e-tests/tests/major-upgrade/42-run-restore.yaml rename to e2e-tests/tests/major-upgrade/43-run-restore.yaml diff --git a/e2e-tests/tests/major-upgrade/44-assert.yaml b/e2e-tests/tests/major-upgrade/44-assert.yaml new file mode 100644 index 0000000000..95d57c293f --- /dev/null +++ b/e2e-tests/tests/major-upgrade/44-assert.yaml @@ -0,0 +1,10 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: 11-read-from-primary +data: + data: ' 100500' diff --git a/e2e-tests/tests/major-upgrade/43-read-from-primary.yaml b/e2e-tests/tests/major-upgrade/44-read-from-primary.yaml similarity index 100% rename from e2e-tests/tests/major-upgrade/43-read-from-primary.yaml rename to e2e-tests/tests/major-upgrade/44-read-from-primary.yaml diff --git a/e2e-tests/tests/scheduled-backup/01-assert.yaml b/e2e-tests/tests/scheduled-backup/01-assert.yaml index 058c649654..6be5e76ed1 100644 --- a/e2e-tests/tests/scheduled-backup/01-assert.yaml +++ b/e2e-tests/tests/scheduled-backup/01-assert.yaml @@ -1,6 +1,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 300 +timeout: 420 --- kind: StatefulSet apiVersion: apps/v1 diff --git a/e2e-tests/tests/scheduled-backup/06-assert.yaml b/e2e-tests/tests/scheduled-backup/06-assert.yaml index 339f6d3e59..1bc8610ba3 100644 --- a/e2e-tests/tests/scheduled-backup/06-assert.yaml +++ b/e2e-tests/tests/scheduled-backup/06-assert.yaml @@ -9,9 +9,6 @@ metadata: spec: pgCluster: scheduled-backup repoName: repo2 -status: - backupType: full - state: Succeeded --- apiVersion: pgv2.percona.com/v2 kind: PerconaPGBackup @@ -34,11 +31,3 @@ spec: status: backupType: full state: Succeeded ---- -apiVersion: postgres-operator.crunchydata.com/v1beta1 -kind: PostgresCluster -metadata: - name: scheduled-backup - generation: 6 -status: - observedGeneration: 6 diff --git a/e2e-tests/tests/scheduled-backup/06-engage-gcs.yaml b/e2e-tests/tests/scheduled-backup/06-engage-gcs.yaml index b723a6f2ad..726f40c4a1 100644 --- a/e2e-tests/tests/scheduled-backup/06-engage-gcs.yaml +++ b/e2e-tests/tests/scheduled-backup/06-engage-gcs.yaml @@ -15,7 +15,7 @@ spec: bucket: pg-operator-testing name: repo2 schedules: - full: "*/5 * * * *" + full: "*/1 * * * *" - azure: container: pg-operator-testing name: repo3 \ No newline at end of file diff --git a/e2e-tests/tests/scheduled-backup/07-assert.yaml b/e2e-tests/tests/scheduled-backup/07-assert.yaml index fab0f46ba4..bcd0d10b80 100644 --- a/e2e-tests/tests/scheduled-backup/07-assert.yaml +++ b/e2e-tests/tests/scheduled-backup/07-assert.yaml @@ -2,20 +2,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert timeout: 600 --- ---- -kind: Job -apiVersion: batch/v1 -metadata: - labels: - postgres-operator.crunchydata.com/pgbackrest-repo: repo2 - ownerReferences: - - apiVersion: pgv2.percona.com/v2 - kind: PerconaPGBackup - controller: true - blockOwnerDeletion: true -status: - succeeded: 1 ---- apiVersion: pgv2.percona.com/v2 kind: PerconaPGBackup metadata: @@ -31,9 +17,6 @@ metadata: spec: pgCluster: scheduled-backup repoName: repo2 -status: - backupType: full - state: Succeeded --- apiVersion: pgv2.percona.com/v2 kind: PerconaPGBackup @@ -56,11 +39,4 @@ spec: status: backupType: full state: Succeeded ---- -apiVersion: postgres-operator.crunchydata.com/v1beta1 -kind: PostgresCluster -metadata: - name: scheduled-backup - generation: 8 -status: - observedGeneration: 8 + diff --git a/e2e-tests/tests/scheduled-backup/08-assert.yaml b/e2e-tests/tests/scheduled-backup/08-assert.yaml index d10f6b8bd7..159f20e98d 100644 --- a/e2e-tests/tests/scheduled-backup/08-assert.yaml +++ b/e2e-tests/tests/scheduled-backup/08-assert.yaml @@ -15,6 +15,19 @@ metadata: status: succeeded: 1 --- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/pgbackrest-repo: repo2 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 +--- apiVersion: pgv2.percona.com/v2 kind: PerconaPGBackup metadata: @@ -26,6 +39,17 @@ status: backupType: full state: Succeeded --- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGBackup +metadata: + generation: 1 +spec: + pgCluster: scheduled-backup + repoName: repo2 +status: + backupType: full + state: Succeeded +--- apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster metadata: diff --git a/e2e-tests/tests/upgrade-consistency/05-assert.yaml b/e2e-tests/tests/upgrade-consistency/05-assert.yaml new file mode 100644 index 0000000000..4a8aef6e28 --- /dev/null +++ b/e2e-tests/tests/upgrade-consistency/05-assert.yaml @@ -0,0 +1,135 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 240 +--- +kind: StatefulSet +apiVersion: apps/v1 +metadata: + name: upgrade-consistency-repo-host + labels: + postgres-operator.crunchydata.com/cluster: upgrade-consistency + postgres-operator.crunchydata.com/data: pgbackrest + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-dedicated: '' + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: upgrade-consistency + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 5 + replicas: 1 + readyReplicas: 1 + updatedReplicas: 1 + collisionCount: 0 +--- +kind: StatefulSet +apiVersion: apps/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: upgrade-consistency + postgres-operator.crunchydata.com/data: postgres + postgres-operator.crunchydata.com/instance-set: instance1 + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: upgrade-consistency + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 5 + replicas: 1 + readyReplicas: 1 + updatedReplicas: 1 + collisionCount: 0 +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: upgrade-consistency-pgbouncer + labels: + postgres-operator.crunchydata.com/cluster: upgrade-consistency + postgres-operator.crunchydata.com/role: pgbouncer + annotations: + deployment.kubernetes.io/revision: '5' + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: upgrade-consistency + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 5 + replicas: 3 + updatedReplicas: 3 + readyReplicas: 3 +--- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: upgrade-consistency + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-backup: replica-create + postgres-operator.crunchydata.com/pgbackrest-repo: repo1 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 +--- +apiVersion: postgres-operator.crunchydata.com/v1beta1 +kind: PostgresCluster +metadata: + name: upgrade-consistency + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGCluster + name: upgrade-consistency + controller: true + blockOwnerDeletion: true + finalizers: + - postgres-operator.crunchydata.com/finalizer +status: + instances: + - name: instance1 + readyReplicas: 3 + replicas: 3 + updatedReplicas: 3 + observedGeneration: 5 + pgbackrest: + repoHost: + apiVersion: apps/v1 + kind: StatefulSet + ready: true + repos: + - bound: true + name: repo1 + replicaCreateBackupComplete: true + stanzaCreated: true + proxy: + pgBouncer: + readyReplicas: 3 + replicas: 3 +--- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + name: upgrade-consistency +spec: + crVersion: 2.5.0 +status: + pgbouncer: + ready: 3 + size: 3 + postgres: + instances: + - name: instance1 + ready: 3 + size: 3 + ready: 3 + size: 3 + state: ready diff --git a/e2e-tests/tests/upgrade-consistency/05-patch-cluster.yaml b/e2e-tests/tests/upgrade-consistency/05-patch-cluster.yaml new file mode 100644 index 0000000000..a5f90f4501 --- /dev/null +++ b/e2e-tests/tests/upgrade-consistency/05-patch-cluster.yaml @@ -0,0 +1,16 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +timeout: 30 +commands: + - script: |- + set -o errexit + set -o xtrace + + source ../../functions + + get_cr \ + | yq eval ' + .spec.crVersion="2.5.0"' - \ + | kubectl -n "${NAMESPACE}" apply -f - + + sleep 10 diff --git a/e2e-tests/tests/upgrade-minor/01-assert.yaml b/e2e-tests/tests/upgrade-minor/01-assert.yaml index 553681c646..19551ad384 100644 --- a/e2e-tests/tests/upgrade-minor/01-assert.yaml +++ b/e2e-tests/tests/upgrade-minor/01-assert.yaml @@ -138,6 +138,7 @@ apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster metadata: name: upgrade-minor + generation: 1 status: pgbouncer: ready: 3 diff --git a/e2e-tests/tests/upgrade-minor/04-assert.yaml b/e2e-tests/tests/upgrade-minor/04-assert.yaml index 064501ea0a..93804ea3e2 100644 --- a/e2e-tests/tests/upgrade-minor/04-assert.yaml +++ b/e2e-tests/tests/upgrade-minor/04-assert.yaml @@ -21,4 +21,4 @@ metadata: name: check-operator-deploy-status timeout: 45 commands: - - script: kubectl assert exist-enhanced deployment percona-postgresql-operator -n ${OPERATOR_NS:-$NAMESPACE} --field-selector status.readyReplicas=1 + - script: kubectl assert exist-enhanced deployment percona-postgresql-operator -n ${OPERATOR_NS:-$NAMESPACE} --field-selector status.readyReplicas=1,status.observedGeneration=2 diff --git a/e2e-tests/tests/upgrade-minor/04-upgrade-operator.yaml b/e2e-tests/tests/upgrade-minor/04-upgrade-operator.yaml index bc93c023cd..7d1a67b0e1 100644 --- a/e2e-tests/tests/upgrade-minor/04-upgrade-operator.yaml +++ b/e2e-tests/tests/upgrade-minor/04-upgrade-operator.yaml @@ -8,5 +8,5 @@ commands: source ../../functions - deploy_operator + update_operator kubectl wait -n ${OPERATOR_NS:-$NAMESPACE} --timeout 30s --for=jsonpath='{.spec.template.spec.containers[0].image}'=$IMAGE deployment/percona-postgresql-operator diff --git a/e2e-tests/tests/upgrade-minor/05-assert.yaml b/e2e-tests/tests/upgrade-minor/05-assert.yaml index 25808acbe7..478ec3551c 100644 --- a/e2e-tests/tests/upgrade-minor/05-assert.yaml +++ b/e2e-tests/tests/upgrade-minor/05-assert.yaml @@ -1,6 +1,6 @@ apiVersion: kuttl.dev/v1beta1 kind: TestAssert -timeout: 300 +timeout: 10 --- kind: StatefulSet apiVersion: apps/v1 @@ -22,7 +22,7 @@ metadata: controller: true blockOwnerDeletion: true status: - observedGeneration: 3 + observedGeneration: 1 replicas: 1 readyReplicas: 1 currentReplicas: 1 @@ -32,7 +32,7 @@ status: kind: StatefulSet apiVersion: apps/v1 metadata: - generation: 3 + generation: 1 labels: app.kubernetes.io/component: pg app.kubernetes.io/instance: upgrade-minor @@ -48,26 +48,13 @@ metadata: name: upgrade-minor controller: true blockOwnerDeletion: true -spec: - template: - metadata: - labels: - app.kubernetes.io/component: pg - app.kubernetes.io/instance: upgrade-minor - app.kubernetes.io/managed-by: percona-postgresql-operator - app.kubernetes.io/name: percona-postgresql - app.kubernetes.io/part-of: percona-postgresql - postgres-operator.crunchydata.com/cluster: upgrade-minor - postgres-operator.crunchydata.com/data: postgres - postgres-operator.crunchydata.com/instance-set: instance1 - postgres-operator.crunchydata.com/patroni: upgrade-minor-ha status: - availableReplicas: 1 - collisionCount: 0 - observedGeneration: 3 - readyReplicas: 1 + observedGeneration: 1 replicas: 1 + readyReplicas: 1 + currentReplicas: 1 updatedReplicas: 1 + collisionCount: 0 --- kind: Deployment apiVersion: apps/v1 @@ -82,7 +69,7 @@ metadata: postgres-operator.crunchydata.com/cluster: upgrade-minor postgres-operator.crunchydata.com/role: pgbouncer annotations: - deployment.kubernetes.io/revision: '3' + deployment.kubernetes.io/revision: '1' ownerReferences: - apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster @@ -90,31 +77,15 @@ metadata: controller: true blockOwnerDeletion: true status: - observedGeneration: 3 + observedGeneration: 1 replicas: 3 updatedReplicas: 3 readyReplicas: 3 --- -kind: Job -apiVersion: batch/v1 -metadata: - labels: - postgres-operator.crunchydata.com/cluster: upgrade-minor - postgres-operator.crunchydata.com/pgbackrest: '' - postgres-operator.crunchydata.com/pgbackrest-backup: replica-create - postgres-operator.crunchydata.com/pgbackrest-repo: repo1 - ownerReferences: - - apiVersion: pgv2.percona.com/v2 - kind: PerconaPGBackup - controller: true - blockOwnerDeletion: true -status: - succeeded: 1 ---- apiVersion: postgres-operator.crunchydata.com/v1beta1 kind: PostgresCluster metadata: - generation: 2 + generation: 1 labels: e2e: upgrade-minor name: upgrade-minor @@ -132,7 +103,7 @@ status: readyReplicas: 3 replicas: 3 updatedReplicas: 3 - observedGeneration: 2 + observedGeneration: 1 pgbackrest: repoHost: apiVersion: apps/v1 @@ -151,17 +122,17 @@ status: apiVersion: pgv2.percona.com/v2 kind: PerconaPGCluster metadata: - generation: 2 name: upgrade-minor + generation: 1 status: pgbouncer: ready: 3 size: 3 postgres: instances: - - name: instance1 - ready: 3 - size: 3 + - name: instance1 + ready: 3 + size: 3 ready: 3 size: 3 - state: ready + state: ready \ No newline at end of file diff --git a/e2e-tests/tests/upgrade-minor/05-sleep-after-operator-update.yaml b/e2e-tests/tests/upgrade-minor/05-sleep-after-operator-update.yaml new file mode 100644 index 0000000000..506a371d4e --- /dev/null +++ b/e2e-tests/tests/upgrade-minor/05-sleep-after-operator-update.yaml @@ -0,0 +1,6 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +timeout: 30 +commands: + - script: |- + sleep 30 diff --git a/e2e-tests/tests/upgrade-minor/06-assert.yaml b/e2e-tests/tests/upgrade-minor/06-assert.yaml new file mode 100644 index 0000000000..5367bb784c --- /dev/null +++ b/e2e-tests/tests/upgrade-minor/06-assert.yaml @@ -0,0 +1,165 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 300 +--- +kind: StatefulSet +apiVersion: apps/v1 +metadata: + name: upgrade-minor-repo-host + labels: + app.kubernetes.io/instance: upgrade-minor + app.kubernetes.io/managed-by: percona-postgresql-operator + app.kubernetes.io/name: percona-postgresql + app.kubernetes.io/part-of: percona-postgresql + postgres-operator.crunchydata.com/cluster: upgrade-minor + postgres-operator.crunchydata.com/data: pgbackrest + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-dedicated: '' + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: upgrade-minor + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 2 + replicas: 1 + readyReplicas: 1 + currentReplicas: 1 + updatedReplicas: 1 + collisionCount: 0 +--- +kind: StatefulSet +apiVersion: apps/v1 +metadata: + generation: 2 + labels: + app.kubernetes.io/component: pg + app.kubernetes.io/instance: upgrade-minor + app.kubernetes.io/managed-by: percona-postgresql-operator + app.kubernetes.io/name: percona-postgresql + app.kubernetes.io/part-of: percona-postgresql + postgres-operator.crunchydata.com/cluster: upgrade-minor + postgres-operator.crunchydata.com/data: postgres + postgres-operator.crunchydata.com/instance-set: instance1 + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: upgrade-minor + controller: true + blockOwnerDeletion: true +spec: + template: + metadata: + labels: + app.kubernetes.io/component: pg + app.kubernetes.io/instance: upgrade-minor + app.kubernetes.io/managed-by: percona-postgresql-operator + app.kubernetes.io/name: percona-postgresql + app.kubernetes.io/part-of: percona-postgresql + postgres-operator.crunchydata.com/cluster: upgrade-minor + postgres-operator.crunchydata.com/data: postgres + postgres-operator.crunchydata.com/instance-set: instance1 + postgres-operator.crunchydata.com/patroni: upgrade-minor-ha +status: + availableReplicas: 1 + collisionCount: 0 + observedGeneration: 2 + readyReplicas: 1 + replicas: 1 + updatedReplicas: 1 +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: upgrade-minor-pgbouncer + labels: + app.kubernetes.io/component: pgbouncer + app.kubernetes.io/instance: upgrade-minor + app.kubernetes.io/managed-by: percona-postgresql-operator + app.kubernetes.io/name: percona-postgresql + app.kubernetes.io/part-of: percona-postgresql + postgres-operator.crunchydata.com/cluster: upgrade-minor + postgres-operator.crunchydata.com/role: pgbouncer + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: upgrade-minor + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 2 + replicas: 3 + updatedReplicas: 3 + readyReplicas: 3 +--- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: upgrade-minor + postgres-operator.crunchydata.com/pgbackrest: '' + postgres-operator.crunchydata.com/pgbackrest-backup: replica-create + postgres-operator.crunchydata.com/pgbackrest-repo: repo1 + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGBackup + controller: true + blockOwnerDeletion: true +status: + succeeded: 1 +--- +apiVersion: postgres-operator.crunchydata.com/v1beta1 +kind: PostgresCluster +metadata: + generation: 2 + labels: + e2e: upgrade-minor + name: upgrade-minor + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGCluster + name: upgrade-minor + controller: true + blockOwnerDeletion: true + finalizers: + - postgres-operator.crunchydata.com/finalizer +status: + instances: + - name: instance1 + readyReplicas: 3 + replicas: 3 + updatedReplicas: 3 + observedGeneration: 2 + pgbackrest: + repoHost: + apiVersion: apps/v1 + kind: StatefulSet + ready: true + repos: + - bound: true + name: repo1 + replicaCreateBackupComplete: true + stanzaCreated: true + proxy: + pgBouncer: + readyReplicas: 3 + replicas: 3 +--- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + generation: 2 + name: upgrade-minor +status: + pgbouncer: + ready: 3 + size: 3 + postgres: + instances: + - name: instance1 + ready: 3 + size: 3 + ready: 3 + size: 3 + state: ready diff --git a/e2e-tests/tests/upgrade-minor/05-upgrade-cluster.yaml b/e2e-tests/tests/upgrade-minor/06-upgrade-cluster.yaml similarity index 100% rename from e2e-tests/tests/upgrade-minor/05-upgrade-cluster.yaml rename to e2e-tests/tests/upgrade-minor/06-upgrade-cluster.yaml diff --git a/e2e-tests/tests/upgrade-minor/06-write-data.yaml b/e2e-tests/tests/upgrade-minor/07-write-data.yaml similarity index 100% rename from e2e-tests/tests/upgrade-minor/06-write-data.yaml rename to e2e-tests/tests/upgrade-minor/07-write-data.yaml diff --git a/e2e-tests/tests/upgrade-minor/07-assert.yaml b/e2e-tests/tests/upgrade-minor/08-assert.yaml similarity index 100% rename from e2e-tests/tests/upgrade-minor/07-assert.yaml rename to e2e-tests/tests/upgrade-minor/08-assert.yaml diff --git a/e2e-tests/tests/upgrade-minor/07-read-from-primary.yaml b/e2e-tests/tests/upgrade-minor/08-read-from-primary.yaml similarity index 100% rename from e2e-tests/tests/upgrade-minor/07-read-from-primary.yaml rename to e2e-tests/tests/upgrade-minor/08-read-from-primary.yaml diff --git a/go.mod b/go.mod index f64ad71c71..9ffcc9911c 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( go.opentelemetry.io/otel/trace v1.30.0 go.uber.org/zap v1.27.0 golang.org/x/crypto v0.27.0 - google.golang.org/grpc v1.66.2 + google.golang.org/grpc v1.67.0 gotest.tools/v3 v3.5.1 k8s.io/api v0.31.1 k8s.io/apimachinery v0.31.1 diff --git a/go.sum b/go.sum index db93d53979..0f537a3e7e 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo= -google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.67.0 h1:IdH9y6PF5MPSdAntIcpjQ+tXO41pcQsfZV2RxtQgVcw= +google.golang.org/grpc v1.67.0/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= diff --git a/internal/controller/postgrescluster/instance.go b/internal/controller/postgrescluster/instance.go index 151b43cbc9..ae5eda0f44 100644 --- a/internal/controller/postgrescluster/instance.go +++ b/internal/controller/postgrescluster/instance.go @@ -1215,6 +1215,7 @@ func (r *Reconciler) reconcileInstance( // containers if err == nil { addNSSWrapper( + cluster, config.PostgresContainerImage(cluster), cluster.Spec.ImagePullPolicy, &instance.Spec.Template) diff --git a/internal/controller/postgrescluster/instance_test.go b/internal/controller/postgrescluster/instance_test.go index 3247410a3d..eb63b7bafb 100644 --- a/internal/controller/postgrescluster/instance_test.go +++ b/internal/controller/postgrescluster/instance_test.go @@ -541,6 +541,9 @@ func TestAddPGBackRestToInstancePodSpec(t *testing.T) { cluster := v1beta1.PostgresCluster{} cluster.Name = "hippo" cluster.Default() + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) certificates := corev1.Secret{} certificates.Name = "some-secret" diff --git a/internal/controller/postgrescluster/pgadmin.go b/internal/controller/postgrescluster/pgadmin.go index 1d76dd90b8..db40d670fc 100644 --- a/internal/controller/postgrescluster/pgadmin.go +++ b/internal/controller/postgrescluster/pgadmin.go @@ -361,6 +361,7 @@ func (r *Reconciler) reconcilePGAdminStatefulSet( // add nss_wrapper init container and add nss_wrapper env vars to the pgAdmin // container addNSSWrapper( + cluster, config.PGAdminContainerImage(cluster), cluster.Spec.ImagePullPolicy, &sts.Spec.Template) diff --git a/internal/controller/postgrescluster/pgbackrest.go b/internal/controller/postgrescluster/pgbackrest.go index daf99e4da6..5cf4b72e26 100644 --- a/internal/controller/postgrescluster/pgbackrest.go +++ b/internal/controller/postgrescluster/pgbackrest.go @@ -640,6 +640,7 @@ func (r *Reconciler) generateRepoHostIntent(ctx context.Context, postgresCluster // add nss_wrapper init container and add nss_wrapper env vars to the pgbackrest // container addNSSWrapper( + postgresCluster, config.PGBackRestContainerImage(postgresCluster), postgresCluster.Spec.ImagePullPolicy, &repo.Spec.Template) @@ -736,7 +737,7 @@ func generateBackupJobSpecIntent(postgresCluster *v1beta1.PostgresCluster, Image: config.PGBackRestContainerImage(postgresCluster), ImagePullPolicy: postgresCluster.Spec.ImagePullPolicy, Name: naming.PGBackRestRepoContainerName, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(postgresCluster.CompareVersion("2.5.0") >= 0), } if postgresCluster.Spec.Backups.PGBackRest.Jobs != nil { @@ -1193,6 +1194,7 @@ func (r *Reconciler) reconcileRestoreJob(ctx context.Context, // add nss_wrapper init container and add nss_wrapper env vars to the pgbackrest restore // container addNSSWrapper( + cluster, config.PGBackRestContainerImage(cluster), cluster.Spec.ImagePullPolicy, &restoreJob.Spec.Template) @@ -1245,7 +1247,7 @@ func (r *Reconciler) generateRestoreJobIntent(cluster *v1beta1.PostgresCluster, Name: naming.PGBackRestRestoreContainerName, VolumeMounts: volumeMounts, Env: []corev1.EnvVar{{Name: "PGHOST", Value: "/tmp"}}, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), Resources: dataSource.Resources, }}, RestartPolicy: corev1.RestartPolicyNever, @@ -2647,8 +2649,7 @@ func (r *Reconciler) reconcileStanzaCreate(ctx context.Context, } // Always attempt to create pgBackRest stanza first - configHashMismatch, err := pgbackrest.Executor(exec).StanzaCreateOrUpgrade(ctx, configHash, - false, postgresCluster) + configHashMismatch, err := pgbackrest.Executor(exec).StanzaCreateOrUpgrade(ctx, configHash, postgresCluster) if err != nil { // record and log any errors resulting from running the stanza-create command r.Recorder.Event(postgresCluster, corev1.EventTypeWarning, EventUnableToCreateStanzas, diff --git a/internal/controller/postgrescluster/pgbackrest_test.go b/internal/controller/postgrescluster/pgbackrest_test.go index 077800964c..1ab3f8c5c5 100644 --- a/internal/controller/postgrescluster/pgbackrest_test.go +++ b/internal/controller/postgrescluster/pgbackrest_test.go @@ -2383,7 +2383,16 @@ func TestCopyConfigurationResources(t *testing.T) { func TestGenerateBackupJobIntent(t *testing.T) { t.Run("empty", func(t *testing.T) { spec := generateBackupJobSpecIntent( - &v1beta1.PostgresCluster{}, v1beta1.PGBackRestRepo{}, + &v1beta1.PostgresCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "", + Namespace: "", + Labels: map[string]string{ + naming.LabelVersion: "2.5.0", + }, + }, + }, + v1beta1.PGBackRestRepo{}, "", nil, nil, ) @@ -2674,7 +2683,13 @@ func TestGenerateRestoreJobIntent(t *testing.T) { } t.Run("empty", func(t *testing.T) { - err := r.generateRestoreJobIntent(&v1beta1.PostgresCluster{}, "", "", + err := r.generateRestoreJobIntent(&v1beta1.PostgresCluster{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + naming.LabelVersion: "2.5.0", + }, + }, + }, "", "", []string{}, []corev1.VolumeMount{}, []corev1.Volume{}, &v1beta1.PostgresClusterDataSource{}, &batchv1.Job{}) assert.NilError(t, err) @@ -2718,6 +2733,9 @@ func TestGenerateRestoreJobIntent(t *testing.T) { cluster := &v1beta1.PostgresCluster{ ObjectMeta: metav1.ObjectMeta{ Name: "test", + Labels: map[string]string{ + naming.LabelVersion: "2.5.0", + }, }, Spec: v1beta1.PostgresClusterSpec{ Metadata: &v1beta1.Metadata{ @@ -2814,7 +2832,7 @@ func TestGenerateRestoreJobIntent(t *testing.T) { }) t.Run("SecurityContext", func(t *testing.T) { assert.DeepEqual(t, job.Spec.Template.Spec.Containers[0].SecurityContext, - initialize.RestrictedSecurityContext()) + initialize.RestrictedSecurityContext(true)) }) t.Run("Resources", func(t *testing.T) { assert.DeepEqual(t, job.Spec.Template.Spec.Containers[0].Resources, @@ -2884,6 +2902,9 @@ func TestObserveRestoreEnv(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: clusterName, Namespace: namespace, + Labels: map[string]string{ + naming.LabelVersion: "2.5.0", + }, }, } meta := naming.PGBackRestRestoreJob(cluster) @@ -3098,6 +3119,9 @@ func TestPrepareForRestore(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: clusterName, Namespace: namespace, + Labels: map[string]string{ + naming.LabelVersion: "2.5.0", + }, }, } meta := naming.PGBackRestRestoreJob(cluster) diff --git a/internal/controller/postgrescluster/pgmonitor.go b/internal/controller/postgrescluster/pgmonitor.go index 94aba727d4..280ce05c50 100644 --- a/internal/controller/postgrescluster/pgmonitor.go +++ b/internal/controller/postgrescluster/pgmonitor.go @@ -270,7 +270,7 @@ func addPGMonitorExporterToInstancePodSpec( withBuiltInCollectors := !strings.EqualFold(cluster.Annotations[naming.PostgresExporterCollectorsAnnotation], "None") - securityContext := initialize.RestrictedSecurityContext() + securityContext := initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0) exporterContainer := corev1.Container{ Name: naming.ContainerPGMonitorExporter, Image: config.PGExporterContainerImage(cluster), diff --git a/internal/controller/postgrescluster/pgmonitor_test.go b/internal/controller/postgrescluster/pgmonitor_test.go index 7d8b6602b8..7dc7645f9a 100644 --- a/internal/controller/postgrescluster/pgmonitor_test.go +++ b/internal/controller/postgrescluster/pgmonitor_test.go @@ -100,6 +100,10 @@ func TestAddPGMonitorExporterToInstancePodSpec(t *testing.T) { cluster.Spec.Port = initialize.Int32(5432) cluster.Spec.ImagePullPolicy = corev1.PullAlways + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) + resources := corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceCPU: resource.MustParse("100m"), diff --git a/internal/controller/postgrescluster/pki.go b/internal/controller/postgrescluster/pki.go index 38dbb28407..40e06b1255 100644 --- a/internal/controller/postgrescluster/pki.go +++ b/internal/controller/postgrescluster/pki.go @@ -21,6 +21,8 @@ import ( "github.com/pkg/errors" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "github.com/percona/percona-postgresql-operator/internal/naming" @@ -69,8 +71,22 @@ func (r *Reconciler) reconcileRootCertificate( } } - err := errors.WithStack(client.IgnoreNotFound( - r.Client.Get(ctx, client.ObjectKeyFromObject(existing), existing))) + err := errors.WithStack( + r.Client.Get(ctx, client.ObjectKeyFromObject(existing), existing)) + // K8SPG-555: we need to check ca certificate from old operator versions + // TODO: remove when 2.4.0 will become unsupported + if k8serrors.IsNotFound(err) { + nn := client.ObjectKeyFromObject(existing) + nn.Name = naming.RootCertSecret + err = errors.WithStack( + r.Client.Get(ctx, nn, existing)) + if err == nil { + existing.Name = naming.RootCertSecret + } + } + if k8serrors.IsNotFound(err) { + err = nil + } root := &pki.RootCertificateAuthority{} @@ -81,17 +97,21 @@ func (r *Reconciler) reconcileRootCertificate( _ = root.Certificate.UnmarshalText(existing.Data[certificateKey]) _ = root.PrivateKey.UnmarshalText(existing.Data[privateKey]) + if cluster.Spec.CustomRootCATLSSecret != nil { + return root, err + } + if !pki.RootIsValid(root) { root, err = pki.NewRootCertificateAuthority() err = errors.WithStack(err) } } - if cluster.Spec.CustomRootCATLSSecret != nil { - return root, err - } intent := &corev1.Secret{ - ObjectMeta: naming.PostgresRootCASecret(cluster), + ObjectMeta: metav1.ObjectMeta{ + Name: existing.Name, + Namespace: existing.Namespace, + }, } intent.SetGroupVersionKind(corev1.SchemeGroupVersion.WithKind("Secret")) intent.Data = make(map[string][]byte) diff --git a/internal/controller/postgrescluster/util.go b/internal/controller/postgrescluster/util.go index 7cb5445b0e..33799a35a0 100644 --- a/internal/controller/postgrescluster/util.go +++ b/internal/controller/postgrescluster/util.go @@ -28,6 +28,7 @@ import ( "github.com/percona/percona-postgresql-operator/internal/initialize" "github.com/percona/percona-postgresql-operator/internal/naming" + "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" ) const ( @@ -219,7 +220,7 @@ func addTMPEmptyDir(template *corev1.PodTemplateSpec, sizeLimit resource.Quantit // containers in the Pod template. Additionally, an init container is added to the Pod template // as needed to setup the nss_wrapper. Please note that the nss_wrapper is required for // compatibility with OpenShift: https://access.redhat.com/articles/4859371. -func addNSSWrapper(image string, imagePullPolicy corev1.PullPolicy, template *corev1.PodTemplateSpec) { +func addNSSWrapper(cluster *v1beta1.PostgresCluster, image string, imagePullPolicy corev1.PullPolicy, template *corev1.PodTemplateSpec) { nssWrapperCmd := postgresNSSWrapperPrefix + nssWrapperScript for i, c := range template.Spec.Containers { @@ -250,7 +251,7 @@ func addNSSWrapper(image string, imagePullPolicy corev1.PullPolicy, template *co Image: image, ImagePullPolicy: imagePullPolicy, Name: naming.ContainerNSSWrapperInit, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), } // Here we set the NSS wrapper container resources to the 'database', 'pgadmin' diff --git a/internal/controller/postgrescluster/util_test.go b/internal/controller/postgrescluster/util_test.go index e6c2076f55..65d8321f2e 100644 --- a/internal/controller/postgrescluster/util_test.go +++ b/internal/controller/postgrescluster/util_test.go @@ -24,9 +24,11 @@ import ( batchv1 "k8s.io/api/batch/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/percona/percona-postgresql-operator/internal/naming" "github.com/percona/percona-postgresql-operator/internal/testing/cmp" + "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" ) func TestSafeHash32(t *testing.T) { @@ -119,6 +121,12 @@ func TestAddDevSHM(t *testing.T) { } func TestAddNSSWrapper(t *testing.T) { + cluster := &v1beta1.PostgresCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "nss-wrapper-test", + Namespace: "nss-wrapper-test", + }, + } image := "test-image" imagePullPolicy := corev1.PullAlways @@ -226,7 +234,7 @@ func TestAddNSSWrapper(t *testing.T) { template := tc.podTemplate beforeAddNSS := template.DeepCopy().Spec.Containers - addNSSWrapper(image, imagePullPolicy, template) + addNSSWrapper(cluster, image, imagePullPolicy, template) t.Run("container-updated", func(t *testing.T) { // Each container that requires the nss_wrapper envs should be updated diff --git a/internal/controller/postgrescluster/volumes.go b/internal/controller/postgrescluster/volumes.go index 467a9f9bb7..ced685e730 100644 --- a/internal/controller/postgrescluster/volumes.go +++ b/internal/controller/postgrescluster/volumes.go @@ -477,7 +477,7 @@ func (r *Reconciler) reconcileMovePGDataDir(ctx context.Context, Image: config.PostgresContainerImage(cluster), ImagePullPolicy: cluster.Spec.ImagePullPolicy, Name: naming.ContainerJobMovePGDataDir, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), VolumeMounts: []corev1.VolumeMount{postgres.DataVolumeMount()}, } if len(cluster.Spec.InstanceSets) > 0 { @@ -596,7 +596,7 @@ func (r *Reconciler) reconcileMoveWALDir(ctx context.Context, Image: config.PostgresContainerImage(cluster), ImagePullPolicy: cluster.Spec.ImagePullPolicy, Name: naming.ContainerJobMovePGWALDir, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), VolumeMounts: []corev1.VolumeMount{postgres.WALVolumeMount()}, } if len(cluster.Spec.InstanceSets) > 0 { @@ -720,7 +720,7 @@ func (r *Reconciler) reconcileMoveRepoDir(ctx context.Context, Image: config.PGBackRestContainerImage(cluster), ImagePullPolicy: cluster.Spec.ImagePullPolicy, Name: naming.ContainerJobMovePGBackRestRepoDir, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), VolumeMounts: []corev1.VolumeMount{pgbackrest.RepoVolumeMount()}, } if cluster.Spec.Backups.PGBackRest.RepoHost != nil { diff --git a/internal/controller/postgrescluster/volumes_test.go b/internal/controller/postgrescluster/volumes_test.go index 4431d13946..f5437c9485 100644 --- a/internal/controller/postgrescluster/volumes_test.go +++ b/internal/controller/postgrescluster/volumes_test.go @@ -49,6 +49,9 @@ func TestHandlePersistentVolumeClaimError(t *testing.T) { cluster := new(v1beta1.PostgresCluster) cluster.Namespace = "ns1" cluster.Name = "pg2" + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) reset := func() { cluster.Status.Conditions = cluster.Status.Conditions[:0] @@ -395,7 +398,7 @@ func TestReconcileConfigureExistingPVCs(t *testing.T) { Name: "testcluster", Namespace: ns.GetName(), Labels: map[string]string{ - naming.LabelVersion: "2.3.0", + naming.LabelVersion: "2.5.0", }, }, Spec: v1beta1.PostgresClusterSpec{ @@ -657,6 +660,9 @@ func TestReconcileMoveDirectories(t *testing.T) { ObjectMeta: metav1.ObjectMeta{ Name: "testcluster", Namespace: ns.GetName(), + Labels: map[string]string{ + naming.LabelVersion: "2.5.0", + }, }, Spec: v1beta1.PostgresClusterSpec{ PostgresVersion: 13, diff --git a/internal/controller/standalone_pgadmin/pod.go b/internal/controller/standalone_pgadmin/pod.go index 024270fc6e..8278264b5d 100644 --- a/internal/controller/standalone_pgadmin/pod.go +++ b/internal/controller/standalone_pgadmin/pod.go @@ -113,7 +113,7 @@ func pod( Image: config.StandalonePGAdminContainerImage(inPGAdmin), ImagePullPolicy: inPGAdmin.Spec.ImagePullPolicy, Resources: inPGAdmin.Spec.Resources, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(true), Ports: []corev1.ContainerPort{{ Name: naming.PortPGAdmin, ContainerPort: int32(pgAdminPort), @@ -180,7 +180,7 @@ func pod( Image: container.Image, ImagePullPolicy: container.ImagePullPolicy, Resources: container.Resources, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(true), VolumeMounts: []corev1.VolumeMount{ // Volume to write a custom `config_system.py` file to. { diff --git a/internal/initialize/security.go b/internal/initialize/security.go index 49291db478..7eb865d308 100644 --- a/internal/initialize/security.go +++ b/internal/initialize/security.go @@ -31,8 +31,8 @@ func PodSecurityContext() *corev1.PodSecurityContext { // RestrictedSecurityContext returns a v1.SecurityContext with safe defaults. // See https://docs.k8s.io/concepts/security/pod-security-standards/ -func RestrictedSecurityContext() *corev1.SecurityContext { - return &corev1.SecurityContext{ +func RestrictedSecurityContext(enableSeccompProfile bool) *corev1.SecurityContext { + secContext := &corev1.SecurityContext{ // Prevent any container processes from gaining privileges. AllowPrivilegeEscalation: Bool(false), @@ -51,9 +51,13 @@ func RestrictedSecurityContext() *corev1.SecurityContext { // Fail to start the container if its image runs as UID 0 (root). RunAsNonRoot: Bool(true), + } - SeccompProfile: &corev1.SeccompProfile{ + if enableSeccompProfile { + secContext.SeccompProfile = &corev1.SeccompProfile{ Type: corev1.SeccompProfileTypeRuntimeDefault, - }, + } } + + return secContext } diff --git a/internal/initialize/security_test.go b/internal/initialize/security_test.go index 66271379d9..7a3a42b997 100644 --- a/internal/initialize/security_test.go +++ b/internal/initialize/security_test.go @@ -53,7 +53,7 @@ func TestPodSecurityContext(t *testing.T) { // > lower-trust users. t.Run("Restricted", func(t *testing.T) { if assert.Check(t, psc.RunAsNonRoot == nil) { - assert.Assert(t, initialize.RestrictedSecurityContext().RunAsNonRoot != nil, + assert.Assert(t, initialize.RestrictedSecurityContext(true).RunAsNonRoot != nil, `RunAsNonRoot should be delegated to the container-level v1.SecurityContext`) } @@ -61,14 +61,14 @@ func TestPodSecurityContext(t *testing.T) { `Containers must not set runAsUser to 0`) if assert.Check(t, psc.SeccompProfile == nil) { - assert.Assert(t, initialize.RestrictedSecurityContext().SeccompProfile != nil, + assert.Assert(t, initialize.RestrictedSecurityContext(true).SeccompProfile != nil, `SeccompProfile should be delegated to the container-level v1.SecurityContext`) } }) } func TestRestrictedSecurityContext(t *testing.T) { - sc := initialize.RestrictedSecurityContext() + sc := initialize.RestrictedSecurityContext(true) // Kubernetes describes recommended security profiles: // - https://docs.k8s.io/concepts/security/pod-security-standards/ diff --git a/internal/naming/names.go b/internal/naming/names.go index 8c1850c370..7f45306895 100644 --- a/internal/naming/names.go +++ b/internal/naming/names.go @@ -90,9 +90,11 @@ const ( ) const ( - // K8SPG-555: use PostgresRootCASecret instead. + // Deprecated: K8SPG-555: use PostgresRootCASecret instead. + // Currently it's used to update certificates from older operator version // RootCertSecret is the default root certificate secret name - // RootCertSecret = "pgo-root-cacert" /* #nosec */ + // TODO: remove when 2.4.0 will become unsupported + RootCertSecret = "pgo-root-cacert" /* #nosec */ // ClusterCertSecret is the default cluster leaf certificate secret name ClusterCertSecret = "%s-cluster-cert" /* #nosec */ diff --git a/internal/pgadmin/reconcile.go b/internal/pgadmin/reconcile.go index 6a237f9118..f07788c42e 100644 --- a/internal/pgadmin/reconcile.go +++ b/internal/pgadmin/reconcile.go @@ -244,7 +244,7 @@ func Pod( ImagePullPolicy: inCluster.Spec.ImagePullPolicy, Resources: inCluster.Spec.UserInterface.PGAdmin.Resources, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(inCluster.CompareVersion("2.5.0") >= 0), Ports: []corev1.ContainerPort{{ Name: naming.PortPGAdmin, @@ -294,7 +294,7 @@ func Pod( Image: container.Image, ImagePullPolicy: container.ImagePullPolicy, Resources: container.Resources, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(inCluster.CompareVersion("2.5.0") >= 0), VolumeMounts: []corev1.VolumeMount{ startupVolumeMount, configVolumeMount, diff --git a/internal/pgadmin/reconcile_test.go b/internal/pgadmin/reconcile_test.go index c94d90c637..c60f77e638 100644 --- a/internal/pgadmin/reconcile_test.go +++ b/internal/pgadmin/reconcile_test.go @@ -22,6 +22,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + "github.com/percona/percona-postgresql-operator/internal/naming" "github.com/percona/percona-postgresql-operator/internal/testing/cmp" "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" ) @@ -85,6 +86,10 @@ func TestPod(t *testing.T) { pod := new(corev1.PodSpec) pvc := new(corev1.PersistentVolumeClaim) + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) + call := func() { Pod(cluster, config, pod, pvc) } t.Run("Disabled", func(t *testing.T) { diff --git a/internal/pgbackrest/config.go b/internal/pgbackrest/config.go index cc976790ee..dbd1b071c0 100644 --- a/internal/pgbackrest/config.go +++ b/internal/pgbackrest/config.go @@ -159,7 +159,7 @@ func MakePGBackrestLogDir(template *corev1.PodTemplateSpec, Image: config.PGBackRestContainerImage(cluster), ImagePullPolicy: cluster.Spec.ImagePullPolicy, Name: naming.ContainerPGBackRestLogDirInit, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), } // Set the container resources to the 'pgbackrest' container configuration. @@ -444,7 +444,7 @@ func getExternalRepoConfigs(repo v1beta1.PGBackRestRepo) map[string]string { // reloadCommand returns an entrypoint that convinces the pgBackRest TLS server // to reload its options and certificate files when they change. The process // will appear as name in `ps` and `top`. -func reloadCommand(name string) []string { +func reloadCommand(name string, post250 bool) []string { // Use a Bash loop to periodically check the mtime of the mounted server // volume and configuration file. When either changes, signal pgBackRest // and print the observed timestamp. @@ -460,7 +460,29 @@ func reloadCommand(name string) []string { // descriptor gets closed and reopened to use the builtin `[ -nt` to check // mtimes. // - https://unix.stackexchange.com/a/407383 - const script = ` + script := ` +exec {fd}<> <(:) +until read -r -t 5 -u "${fd}"; do + if + [ "${filename}" -nt "/proc/self/fd/${fd}" ] && + pkill -HUP --exact --parent=0 pgbackrest + then + exec {fd}>&- && exec {fd}<> <(:) + stat --dereference --format='Loaded configuration dated %y' "${filename}" + elif + { [ "${directory}" -nt "/proc/self/fd/${fd}" ] || + [ "${authority}" -nt "/proc/self/fd/${fd}" ] + } && + pkill -HUP --exact --parent=0 pgbackrest + then + exec {fd}>&- && exec {fd}<> <(:) + stat --format='Loaded certificates dated %y' "${directory}" + fi +done +` + + if post250 { + script = ` exec {fd}<> <(:||:) until read -r -t 5 -u "${fd}"; do if @@ -480,6 +502,7 @@ until read -r -t 5 -u "${fd}"; do fi done ` + } // Elide the above script from `ps` and `top` by wrapping it in a function // and calling that. diff --git a/internal/pgbackrest/config_test.go b/internal/pgbackrest/config_test.go index b7d3d319ee..e7bdfb75a5 100644 --- a/internal/pgbackrest/config_test.go +++ b/internal/pgbackrest/config_test.go @@ -320,7 +320,7 @@ func TestMakePGBackrestLogDir(t *testing.T) { func TestReloadCommand(t *testing.T) { shellcheck := require.ShellCheck(t) - command := reloadCommand("some-name") + command := reloadCommand("some-name", true) // Expect a bash command with an inline script. assert.DeepEqual(t, command[:3], []string{"bash", "-ceu", "--"}) @@ -338,7 +338,7 @@ func TestReloadCommand(t *testing.T) { } func TestReloadCommandPrettyYAML(t *testing.T) { - b, err := yaml.Marshal(reloadCommand("any")) + b, err := yaml.Marshal(reloadCommand("any", true)) assert.NilError(t, err) assert.Assert(t, strings.Contains(string(b), "\n- |"), "expected literal block scalar, got:\n%s", b) diff --git a/internal/pgbackrest/pgbackrest.go b/internal/pgbackrest/pgbackrest.go index 675d1b7b74..6dfbad7eb8 100644 --- a/internal/pgbackrest/pgbackrest.go +++ b/internal/pgbackrest/pgbackrest.go @@ -20,11 +20,9 @@ import ( "context" "fmt" "io" - "strings" "github.com/pkg/errors" - "github.com/percona/percona-postgresql-operator/internal/logging" "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" ) @@ -36,10 +34,6 @@ const ( // errMsgStaleReposWithVolumesConfig is the error message displayed when a volume-backed repo has been // configured, but the configuration has not yet propagated into the container. errMsgStaleReposWithVolumesConfig = "postgres operator error: pgBackRest stale volume-backed repo configuration" - - // errMsgBackupDbMismatch is the error message returned from pgBackRest when PG versions - // or PG system identifiers do not match between the PG instance and the existing stanza - errMsgBackupDbMismatch = "backup and archive info files exist but do not match the database" ) // Executor calls "pgbackrest" commands @@ -58,15 +52,10 @@ type Executor func( // from running (with a config mismatch indicating that the pgBackRest configuration as stored in // the cluster's pgBackRest ConfigMap has not yet propagated to the Pod). func (exec Executor) StanzaCreateOrUpgrade(ctx context.Context, configHash string, - upgrade bool, postgresCluster *v1beta1.PostgresCluster) (bool, error) { + postgresCluster *v1beta1.PostgresCluster) (bool, error) { var stdout, stderr bytes.Buffer - stanzaCmd := "create" - if upgrade { - stanzaCmd = "upgrade" - } - var reposWithVolumes []v1beta1.PGBackRestRepo for _, repo := range postgresCluster.Spec.Backups.PGBackRest.Repos { if repo.Volume != nil { @@ -95,24 +84,21 @@ func (exec Executor) StanzaCreateOrUpgrade(ctx context.Context, configHash strin // Otherwise, it runs the pgbackrest command, which will either be "stanza-create" or // "stanza-upgrade", depending on the value of the boolean "upgrade" parameter. const script = ` -declare -r hash="$1" stanza="$2" hash_msg="$3" vol_msg="$4" cmd="$5" check_repo_cmd="$6" +declare -r hash="$1" stanza="$2" hash_msg="$3" vol_msg="$4" check_repo_cmd="$5" if [[ "$(< /etc/pgbackrest/conf.d/config-hash)" != "${hash}" ]]; then printf >&2 "%s" "${hash_msg}"; exit 1; elif ! bash -c "${check_repo_cmd}"; then printf >&2 "%s" "${vol_msg}"; exit 1; else - pgbackrest "${cmd}" --stanza="${stanza}" + pgbackrest stanza-create --stanza="${stanza}" || pgbackrest stanza-upgrade --stanza="${stanza}" fi ` - log := logging.FromContext(ctx) if err := exec(ctx, nil, &stdout, &stderr, "bash", "-ceu", "--", script, "-", configHash, DefaultStanzaName, errMsgConfigHashMismatch, errMsgStaleReposWithVolumesConfig, - fmt.Sprintf("stanza-%s", stanzaCmd), checkRepoCmd); err != nil { + checkRepoCmd); err != nil { errReturn := stdout.String() + " " + stderr.String() - log.Error(err, "stanza command failed", "cmd", stanzaCmd, "err", errReturn) - // if the config hashes didn't match, return true and don't return an error since this is // expected while waiting for config changes in ConfigMaps and Secrets to make it to the // container @@ -126,12 +112,6 @@ fi return true, nil } - // if the err returned from pgbackrest command is about a version mismatch - // then we should run upgrade rather than create - if strings.Contains(errReturn, errMsgBackupDbMismatch) { - return exec.StanzaCreateOrUpgrade(ctx, configHash, true, postgresCluster) - } - // if none of the above errors, return the err return false, errors.WithStack(fmt.Errorf("%w: %v", err, errReturn)) } diff --git a/internal/pgbackrest/pgbackrest_test.go b/internal/pgbackrest/pgbackrest_test.go index 85ae724a67..f86b423d68 100644 --- a/internal/pgbackrest/pgbackrest_test.go +++ b/internal/pgbackrest/pgbackrest_test.go @@ -37,18 +37,17 @@ func TestStanzaCreateOrUpgrade(t *testing.T) { ctx := context.Background() configHash := "7f5d4d5bdc" expectedCommand := []string{"bash", "-ceu", "--", ` -declare -r hash="$1" stanza="$2" hash_msg="$3" vol_msg="$4" cmd="$5" check_repo_cmd="$6" +declare -r hash="$1" stanza="$2" hash_msg="$3" vol_msg="$4" check_repo_cmd="$5" if [[ "$(< /etc/pgbackrest/conf.d/config-hash)" != "${hash}" ]]; then printf >&2 "%s" "${hash_msg}"; exit 1; elif ! bash -c "${check_repo_cmd}"; then printf >&2 "%s" "${vol_msg}"; exit 1; else - pgbackrest "${cmd}" --stanza="${stanza}" + pgbackrest stanza-create --stanza="${stanza}" || pgbackrest stanza-upgrade --stanza="${stanza}" fi `, "-", "7f5d4d5bdc", "db", "postgres operator error: pgBackRest config hash mismatch", "postgres operator error: pgBackRest stale volume-backed repo configuration", - "stanza-create", "grep repo1-path /etc/pgbackrest/conf.d/pgbackrest_instance.conf", } @@ -93,7 +92,7 @@ fi }, } - configHashMismatch, err := Executor(stanzaExec).StanzaCreateOrUpgrade(ctx, configHash, false, postgresCluster) + configHashMismatch, err := Executor(stanzaExec).StanzaCreateOrUpgrade(ctx, configHash, postgresCluster) assert.NilError(t, err) assert.Assert(t, !configHashMismatch) diff --git a/internal/pgbackrest/reconcile.go b/internal/pgbackrest/reconcile.go index 78a6ebf967..5e57865f85 100644 --- a/internal/pgbackrest/reconcile.go +++ b/internal/pgbackrest/reconcile.go @@ -125,6 +125,10 @@ func AddConfigToInstancePod( Path: serverConfigProjectionPath, }) secret.Secret.Items = append(secret.Secret.Items, clientCertificates()...) + if cluster.CompareVersion("2.5.0") < 0 { + t := true + secret.Secret.Optional = &t + } // Start with a copy of projections specified in the cluster. Items later in // the list take precedence over earlier items (that is, last write wins). @@ -304,7 +308,7 @@ func addServerContainerAndVolume( Command: []string{"pgbackrest", "server"}, Image: config.PGBackRestContainerImage(cluster), ImagePullPolicy: cluster.Spec.ImagePullPolicy, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), LivenessProbe: &corev1.Probe{ ProbeHandler: corev1.ProbeHandler{ @@ -342,10 +346,10 @@ func addServerContainerAndVolume( reloader := corev1.Container{ Name: naming.ContainerPGBackRestConfig, - Command: reloadCommand(naming.ContainerPGBackRestConfig), + Command: reloadCommand(naming.ContainerPGBackRestConfig, cluster.CompareVersion("2.5.0") >= 0), Image: container.Image, ImagePullPolicy: container.ImagePullPolicy, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(cluster.CompareVersion("2.5.0") >= 0), // The configuration mount is appended by [addConfigVolumeAndMounts]. VolumeMounts: []corev1.VolumeMount{serverVolumeMount}, diff --git a/internal/pgbackrest/reconcile_test.go b/internal/pgbackrest/reconcile_test.go index 66fc2b62e3..da8a4631b0 100644 --- a/internal/pgbackrest/reconcile_test.go +++ b/internal/pgbackrest/reconcile_test.go @@ -180,7 +180,13 @@ func TestAddRepoVolumesToPod(t *testing.T) { } func TestAddConfigToInstancePod(t *testing.T) { - cluster := v1beta1.PostgresCluster{} + cluster := v1beta1.PostgresCluster{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + naming.LabelVersion: "2.5.0", + }, + }, + } cluster.Name = "hippo" cluster.Default() @@ -580,6 +586,9 @@ func TestAddServerToInstancePod(t *testing.T) { cluster := v1beta1.PostgresCluster{} cluster.Name = "hippo" cluster.Default() + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) pod := corev1.PodSpec{ Containers: []corev1.Container{ @@ -840,6 +849,9 @@ func TestAddServerToRepoPod(t *testing.T) { cluster := v1beta1.PostgresCluster{} cluster.Name = "hippo" cluster.Default() + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) pod := corev1.PodSpec{ Containers: []corev1.Container{ diff --git a/internal/pgbouncer/config.go b/internal/pgbouncer/config.go index 2beb348b67..399f5745ae 100644 --- a/internal/pgbouncer/config.go +++ b/internal/pgbouncer/config.go @@ -239,7 +239,7 @@ func podConfigFiles( // reloadCommand returns an entrypoint that convinces PgBouncer to reload // configuration files. The process will appear as name in `ps` and `top`. -func reloadCommand(name string) []string { +func reloadCommand(name string, post250 bool) []string { // Use a Bash loop to periodically check the mtime of the mounted // configuration volume. When it changes, signal PgBouncer and print the // observed timestamp. @@ -249,7 +249,18 @@ func reloadCommand(name string) []string { // descriptor gets closed and reopened to use the builtin `[ -nt` to check // mtimes. // - https://unix.stackexchange.com/a/407383 - const script = ` + script := ` +exec {fd}<> <(:) +while read -r -t 5 -u "${fd}" || true; do + if [ "${directory}" -nt "/proc/self/fd/${fd}" ] && pkill -HUP --exact pgbouncer + then + exec {fd}>&- && exec {fd}<> <(:) + stat --format='Loaded configuration dated %y' "${directory}" + fi +done +` + if post250 { + script = ` exec {fd}<> <(:||:) while read -r -t 5 -u "${fd}" ||:; do if [[ "${directory}" -nt "/proc/self/fd/${fd}" ]] && pkill -HUP --exact pgbouncer @@ -259,6 +270,7 @@ while read -r -t 5 -u "${fd}" ||:; do fi done ` + } // Elide the above script from `ps` and `top` by wrapping it in a function // and calling that. diff --git a/internal/pgbouncer/config_test.go b/internal/pgbouncer/config_test.go index 3f3b8320f5..3c5dddf5f6 100644 --- a/internal/pgbouncer/config_test.go +++ b/internal/pgbouncer/config_test.go @@ -212,7 +212,7 @@ func TestPodConfigFiles(t *testing.T) { func TestReloadCommand(t *testing.T) { shellcheck := require.ShellCheck(t) - command := reloadCommand("some-name") + command := reloadCommand("some-name", true) // Expect a bash command with an inline script. assert.DeepEqual(t, command[:3], []string{"bash", "-ceu", "--"}) diff --git a/internal/pgbouncer/reconcile.go b/internal/pgbouncer/reconcile.go index f617af6e79..37439756fb 100644 --- a/internal/pgbouncer/reconcile.go +++ b/internal/pgbouncer/reconcile.go @@ -145,7 +145,7 @@ func Pod( Image: config.PGBouncerContainerImage(inCluster), ImagePullPolicy: inCluster.Spec.ImagePullPolicy, Resources: inCluster.Spec.Proxy.PGBouncer.Resources, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(inCluster.CompareVersion("2.5.0") >= 0), Ports: []corev1.ContainerPort{{ Name: naming.PortPGBouncer, @@ -162,10 +162,10 @@ func Pod( reloader := corev1.Container{ Name: naming.ContainerPGBouncerConfig, - Command: reloadCommand(naming.ContainerPGBouncerConfig), + Command: reloadCommand(naming.ContainerPGBouncerConfig, inCluster.CompareVersion("2.5.0") >= 0), Image: container.Image, ImagePullPolicy: container.ImagePullPolicy, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(inCluster.CompareVersion("2.5.0") >= 0), VolumeMounts: []corev1.VolumeMount{configVolumeMount}, } diff --git a/internal/pgbouncer/reconcile_test.go b/internal/pgbouncer/reconcile_test.go index 53e54b0ace..ebd97432c8 100644 --- a/internal/pgbouncer/reconcile_test.go +++ b/internal/pgbouncer/reconcile_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" "github.com/percona/percona-postgresql-operator/internal/feature" + "github.com/percona/percona-postgresql-operator/internal/naming" "github.com/percona/percona-postgresql-operator/internal/pki" "github.com/percona/percona-postgresql-operator/internal/postgres" "github.com/percona/percona-postgresql-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1" @@ -112,6 +113,10 @@ func TestPod(t *testing.T) { secret := new(corev1.Secret) pod := new(corev1.PodSpec) + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) + call := func() { Pod(ctx, cluster, configMap, primaryCertificate, secret, pod) } t.Run("Disabled", func(t *testing.T) { diff --git a/internal/postgres/config.go b/internal/postgres/config.go index c4dd5621eb..3ab3fe26e2 100644 --- a/internal/postgres/config.go +++ b/internal/postgres/config.go @@ -52,6 +52,15 @@ recreate() ( // bashSafeLink is a Bash function that moves an existing file or directory // and replaces it with a symbolic link. + bashSafeLinkPre250 = ` +safelink() ( + local desired="$1" name="$2" current + current=$(realpath "${name}") + if [ "${current}" = "${desired}" ]; then return; fi + set -x; mv --no-target-directory "${current}" "${desired}" + ln --no-dereference --force --symbolic "${desired}" "${name}" +) +` bashSafeLink = ` safelink() ( local desired="$1" name="$2" current @@ -154,7 +163,7 @@ func Environment(cluster *v1beta1.PostgresCluster) []corev1.EnvVar { // reloadCommand returns an entrypoint that convinces PostgreSQL to reload // certificate files when they change. The process will appear as name in `ps` // and `top`. -func reloadCommand(name string) []string { +func reloadCommand(name string, post250 bool) []string { // Use a Bash loop to periodically check the mtime of the mounted // certificate volume. When it changes, copy the replication certificate, // signal PostgreSQL, and print the observed timestamp. @@ -178,6 +187,27 @@ func reloadCommand(name string) []string { // mtimes. // - https://unix.stackexchange.com/a/407383 script := fmt.Sprintf(` +declare -r directory=%q +exec {fd}<> <(:) +while read -r -t 5 -u "${fd}" || true; do + if [ "${directory}" -nt "/proc/self/fd/${fd}" ] && + install -D --mode=0600 -t %q "${directory}"/{%s,%s,%s} && + pkill -HUP --exact --parent=1 postgres + then + exec {fd}>&- && exec {fd}<> <(:) + stat --format='Loaded certificates dated %%y' "${directory}" + fi +done +`, + naming.CertMountPath, + naming.ReplicationTmp, + naming.ReplicationCertPath, + naming.ReplicationPrivateKeyPath, + naming.ReplicationCACertPath, + ) + + if post250 { + script = fmt.Sprintf(` # Parameters for curl when managing autogrow annotation. APISERVER="https://kubernetes.default.svc" SERVICEACCOUNT="/var/run/secrets/kubernetes.io/serviceaccount" @@ -213,12 +243,13 @@ while read -r -t 5 -u "${fd}" ||:; do fi done `, - naming.CertMountPath, - naming.ReplicationTmp, - naming.ReplicationCertPath, - naming.ReplicationPrivateKeyPath, - naming.ReplicationCACertPath, - ) + naming.CertMountPath, + naming.ReplicationTmp, + naming.ReplicationCertPath, + naming.ReplicationPrivateKeyPath, + naming.ReplicationCACertPath, + ) + } // Elide the above script from `ps` and `top` by wrapping it in a function // and calling that. @@ -231,7 +262,9 @@ done // PostgreSQL. func startupCommand( ctx context.Context, - cluster *v1beta1.PostgresCluster, instance *v1beta1.PostgresInstanceSetSpec, + cluster *v1beta1.PostgresCluster, + instance *v1beta1.PostgresInstanceSetSpec, + post250 bool, ) []string { version := fmt.Sprint(cluster.Spec.PostgresVersion) walDir := WALDirectory(cluster, instance) @@ -290,7 +323,7 @@ chmod +x /tmp/pg_rewind_tde.sh } args := []string{version, walDir, naming.PGBackRestPGDataLogPath} - script := strings.Join([]string{ + statements := []string{ `declare -r expected_major_version="$1" pgwal_directory="$2" pgbrLog_directory="$3"`, // Function to print the permissions of a file or directory and its parents. @@ -306,36 +339,94 @@ chmod +x /tmp/pg_rewind_tde.sh strings.TrimSpace(bashRecreateDirectory), // Function to change a directory symlink while keeping the directory contents. - strings.TrimSpace(bashSafeLink), + func() string { + if !post250 { + return strings.TrimSpace(bashSafeLinkPre250) + } + return strings.TrimSpace(bashSafeLink) + }(), // Log the effective user ID and all the group IDs. `echo Initializing ...`, - `results 'uid' "$(id -u ||:)" 'gid' "$(id -G ||:)"`, - - // The pgbackrest spool path should be co-located with wal. If a wal volume exists, symlink the spool-path to it. - `if [[ "${pgwal_directory}" == *"pgwal/"* ]] && [[ ! -d "/pgwal/pgbackrest-spool" ]];then rm -rf "/pgdata/pgbackrest-spool" && mkdir -p "/pgwal/pgbackrest-spool" && ln --force --symbolic "/pgwal/pgbackrest-spool" "/pgdata/pgbackrest-spool";fi`, - // When a pgwal volume is removed, the symlink will be broken; force pgbackrest to recreate spool-path. - `if [[ ! -e "/pgdata/pgbackrest-spool" ]];then rm -rf /pgdata/pgbackrest-spool;fi`, + func() string { + if !post250 { + return `results 'uid' "$(id -u)" 'gid' "$(id -G)"` + } + return `results 'uid' "$(id -u ||:)" 'gid' "$(id -G ||:)"` + }(), + + func() string { + if !post250 { + return "remove" + } + + // The pgbackrest spool path should be co-located with wal. If a wal volume exists, symlink the spool-path to it. + return `if [[ "${pgwal_directory}" == *"pgwal/"* ]] && [[ ! -d "/pgwal/pgbackrest-spool" ]];then rm -rf "/pgdata/pgbackrest-spool" && mkdir -p "/pgwal/pgbackrest-spool" && ln --force --symbolic "/pgwal/pgbackrest-spool" "/pgdata/pgbackrest-spool";fi` + }(), + + func() string { + if !post250 { + return "remove" + } + + // When a pgwal volume is removed, the symlink will be broken; force pgbackrest to recreate spool-path. + return `if [[ ! -e "/pgdata/pgbackrest-spool" ]];then rm -rf /pgdata/pgbackrest-spool;fi` + }(), + + func() string { + if !post250 { + return `results 'postgres path' "$(command -v postgres)"` + } + + return `results 'postgres path' "$(command -v postgres ||:)"` + }(), + + func() string { + if !post250 { + return `results 'postgres version' "${postgres_version:=$(postgres --version)}"` + } + + return `results 'postgres version' "${postgres_version:=$(postgres --version ||:)}"` + }(), // Abort when the PostgreSQL version installed in the image does not // match the cluster spec. - `results 'postgres path' "$(command -v postgres ||:)"`, - `results 'postgres version' "${postgres_version:=$(postgres --version ||:)}"`, `[[ "${postgres_version}" =~ ") ${expected_major_version}"($|[^0-9]) ]] ||`, `halt Expected PostgreSQL version "${expected_major_version}"`, - // Abort when the configured data directory is not $PGDATA. - // - https://www.postgresql.org/docs/current/runtime-config-file-locations.html `results 'config directory' "${PGDATA:?}"`, - `postgres_data_directory=$([[ -d "${PGDATA}" ]] && postgres -C data_directory || echo "${PGDATA}")`, + + func() string { + if !post250 { + return `postgres_data_directory=$([ -d "${PGDATA}" ] && postgres -C data_directory || echo "${PGDATA}")` + } + + return `postgres_data_directory=$([[ -d "${PGDATA}" ]] && postgres -C data_directory || echo "${PGDATA}")` + }(), + `results 'data directory' "${postgres_data_directory}"`, + // Abort when the configured data directory is not $PGDATA. + // - https://www.postgresql.org/docs/current/runtime-config-file-locations.html `[[ "${postgres_data_directory}" == "${PGDATA}" ]] ||`, `halt Expected matching config and data directories`, // Determine if the data directory has been prepared for bootstrapping the cluster `bootstrap_dir="${postgres_data_directory}_bootstrap"`, - `[[ -d "${bootstrap_dir}" ]] && results 'bootstrap directory' "${bootstrap_dir}"`, - `[[ -d "${bootstrap_dir}" ]] && postgres_data_directory="${bootstrap_dir}"`, + func() string { + if !post250 { + return `[ -d "${bootstrap_dir}" ] && results 'bootstrap directory' "${bootstrap_dir}"` + } + + return `[[ -d "${bootstrap_dir}" ]] && results 'bootstrap directory' "${bootstrap_dir}"` + }(), + + func() string { + if !post250 { + return `[ -d "${bootstrap_dir}" ] && postgres_data_directory="${bootstrap_dir}"` + } + + return `[[ -d "${bootstrap_dir}" ]] && postgres_data_directory="${bootstrap_dir}"` + }(), // PostgreSQL requires its directory to be writable by only itself. // Pod "securityContext.fsGroup" sets g+w on directories for *some* @@ -381,11 +472,24 @@ chmod +x /tmp/pg_rewind_tde.sh naming.ReplicationCACert), // Add the pg_rewind wrapper script, if TDE is enabled. - pg_rewind_override, + func() string { + if len(pg_rewind_override) < 1 { + return "remove" + } + + return pg_rewind_override + }(), tablespaceCmd, + // When the data directory is empty, there's nothing more to do. - `[[ -f "${postgres_data_directory}/PG_VERSION" ]] || exit 0`, + func() string { + if !post250 { + return `[ -f "${postgres_data_directory}/PG_VERSION" ] || exit 0` + } + + return `[[ -f "${postgres_data_directory}/PG_VERSION" ]] || exit 0` + }(), // Abort when the data directory is not empty and its version does not // match the cluster spec. @@ -409,7 +513,13 @@ chmod +x /tmp/pg_rewind_tde.sh // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/bin/initdb/initdb.c;hb=REL_13_0#l2718 // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/bin/pg_basebackup/pg_basebackup.c;hb=REL_13_0#l2621 `safelink "${pgwal_directory}" "${postgres_data_directory}/pg_wal"`, - `results 'wal directory' "$(realpath "${postgres_data_directory}/pg_wal" ||:)"`, + func() string { + if !post250 { + return `results 'wal directory' "$(realpath "${postgres_data_directory}/pg_wal")"` + } + + return `results 'wal directory' "$(realpath "${postgres_data_directory}/pg_wal" ||:)"` + }(), // Early versions of PGO create replicas with a recovery signal file. // Patroni also creates a standby signal file before starting Postgres, @@ -419,7 +529,17 @@ chmod +x /tmp/pg_rewind_tde.sh // - https://git.postgresql.org/gitweb/?p=postgresql.git;f=src/backend/access/transam/xlog.c;hb=REL_12_0#l5318 // TODO(cbandy): Remove this after 5.0 is EOL. `rm -f "${postgres_data_directory}/recovery.signal"`, - }, "\n") + } + script := strings.Join(filter(statements, func(s string) bool { return s != "remove" }), "\n") return append([]string{"bash", "-ceu", "--", script, "startup"}, args...) } + +func filter(ss []string, test func(string) bool) (ret []string) { + for _, s := range ss { + if test(s) { + ret = append(ret, s) + } + } + return ret +} diff --git a/internal/postgres/config_test.go b/internal/postgres/config_test.go index f03887a474..03849485f2 100644 --- a/internal/postgres/config_test.go +++ b/internal/postgres/config_test.go @@ -473,7 +473,7 @@ func TestStartupCommand(t *testing.T) { instance := new(v1beta1.PostgresInstanceSetSpec) ctx := context.Background() - command := startupCommand(ctx, cluster, instance) + command := startupCommand(ctx, cluster, instance, true) // Expect a bash command with an inline script. assert.DeepEqual(t, command[:3], []string{"bash", "-ceu", "--"}) @@ -508,7 +508,7 @@ func TestStartupCommand(t *testing.T) { }, }, } - command := startupCommand(ctx, cluster, instance) + command := startupCommand(ctx, cluster, instance, true) assert.Assert(t, len(command) > 3) assert.Assert(t, strings.Contains(command[3], `cat << "EOF" > /tmp/pg_rewind_tde.sh #!/bin/sh diff --git a/internal/postgres/reconcile.go b/internal/postgres/reconcile.go index a898a4cba4..b84bd0a73a 100644 --- a/internal/postgres/reconcile.go +++ b/internal/postgres/reconcile.go @@ -185,20 +185,24 @@ func InstancePod(ctx context.Context, Protocol: corev1.ProtocolTCP, }}, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(inCluster.CompareVersion("2.5.0") >= 0), VolumeMounts: dbContainerMounts, } reloader := corev1.Container{ Name: naming.ContainerClientCertCopy, - Command: reloadCommand(naming.ContainerClientCertCopy), + Command: reloadCommand(naming.ContainerClientCertCopy, inCluster.CompareVersion("2.5.0") >= 0), Image: container.Image, ImagePullPolicy: container.ImagePullPolicy, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(inCluster.CompareVersion("2.5.0") >= 0), - VolumeMounts: []corev1.VolumeMount{certVolumeMount, dataVolumeMount}, + VolumeMounts: []corev1.VolumeMount{certVolumeMount}, + } + + if inCluster.CompareVersion("2.5.0") >= 0 { + reloader.VolumeMounts = append(reloader.VolumeMounts, dataVolumeMount) } if inInstanceSpec.Sidecars != nil && @@ -210,13 +214,13 @@ func InstancePod(ctx context.Context, startup := corev1.Container{ Name: naming.ContainerPostgresStartup, - Command: startupCommand(ctx, inCluster, inInstanceSpec), + Command: startupCommand(ctx, inCluster, inInstanceSpec, inCluster.CompareVersion("2.5.0") >= 0), Env: Environment(inCluster), Image: container.Image, ImagePullPolicy: container.ImagePullPolicy, Resources: container.Resources, - SecurityContext: initialize.RestrictedSecurityContext(), + SecurityContext: initialize.RestrictedSecurityContext(inCluster.CompareVersion("2.5.0") >= 0), VolumeMounts: []corev1.VolumeMount{certVolumeMount, dataVolumeMount}, } diff --git a/internal/postgres/reconcile_test.go b/internal/postgres/reconcile_test.go index 67ae10cd4c..fab30edd03 100644 --- a/internal/postgres/reconcile_test.go +++ b/internal/postgres/reconcile_test.go @@ -77,6 +77,9 @@ func TestInstancePod(t *testing.T) { cluster.Default() cluster.Spec.ImagePullPolicy = corev1.PullAlways cluster.Spec.PostgresVersion = 11 + cluster.SetLabels(map[string]string{ + naming.LabelVersion: "2.5.0", + }) dataVolume := new(corev1.PersistentVolumeClaim) dataVolume.Name = "datavol" @@ -280,7 +283,6 @@ initContainers: halt "$(permissions "${pgbrLog_directory}" ||:)" install -D --mode=0600 -t "/tmp/replication" "/pgconf/tls/replication"/{tls.crt,tls.key,ca.crt} - [[ -f "${postgres_data_directory}/PG_VERSION" ]] || exit 0 results 'data version' "${postgres_data_version:=$(< "${postgres_data_directory}/PG_VERSION")}" [[ "${postgres_data_version}" == "${expected_major_version}" ]] || diff --git a/percona/controller/pgcluster/controller.go b/percona/controller/pgcluster/controller.go index 3c1250ba25..1d5d1dac92 100644 --- a/percona/controller/pgcluster/controller.go +++ b/percona/controller/pgcluster/controller.go @@ -213,6 +213,10 @@ func (r *PGClusterReconciler) Reconcile(ctx context.Context, request reconcile.R return reconcile.Result{}, nil } + if err := r.reconcileTLS(ctx, cr); err != nil { + return reconcile.Result{}, errors.Wrap(err, "reconcile TLS") + } + if err := r.reconcileExternalWatchers(ctx, cr); err != nil { return reconcile.Result{}, errors.Wrap(err, "start external watchers") } @@ -282,6 +286,136 @@ func (r *PGClusterReconciler) Reconcile(ctx context.Context, request reconcile.R return ctrl.Result{}, nil } +func (r *PGClusterReconciler) reconcileTLS(ctx context.Context, cr *v2.PerconaPGCluster) error { + if err := r.validateTLS(ctx, cr); err != nil { + return errors.Wrap(err, "validate TLS") + } + if err := r.reconcileOldCACert(ctx, cr); err != nil { + return errors.Wrap(err, "reconcile old CA") + } + return nil +} + +func (r *PGClusterReconciler) validateTLS(ctx context.Context, cr *v2.PerconaPGCluster) error { + validateSecretProjection := func(p *corev1.SecretProjection, requiredPaths ...string) error { + if p == nil { + return nil + } + + if p.Name == "" { + return errors.New("secret name is not specified") + } + + secret := new(corev1.Secret) + nn := types.NamespacedName{Name: p.Name, Namespace: cr.Namespace} + if err := r.Client.Get(ctx, nn, secret); err != nil { + return errors.Wrapf(err, "failed to get secret %s", nn.Name) + } + + pathMap := make(map[string]struct{}) + for _, item := range p.Items { + if _, ok := secret.Data[item.Key]; !ok { + return errors.Errorf("key %s doesn't exist in secret %s", item.Key, secret.Name) + } + pathMap[item.Path] = struct{}{} + } + + for _, path := range requiredPaths { + if _, ok := pathMap[path]; !ok { + if _, ok := secret.Data[path]; !ok { + return errors.Errorf("required path %s was not found both in secret %s and in the .items section", path, secret.Name) + } + } + } + + return nil + } + + if err := validateSecretProjection(cr.Spec.Secrets.CustomRootCATLSSecret, "root.crt", "root.key"); err != nil { + return errors.Wrap(err, "failed to validate .spec.customRootCATLSSecret") + } + + certPaths := []string{"tls.key", "tls.crt"} + if cr.Spec.Secrets.CustomRootCATLSSecret == nil { + certPaths = append(certPaths, "ca.crt") + } + if err := validateSecretProjection(cr.Spec.Secrets.CustomTLSSecret, certPaths...); err != nil { + return errors.Wrap(err, "failed to validate .spec.customTLSSecret") + } + if err := validateSecretProjection(cr.Spec.Secrets.CustomReplicationClientTLSSecret, certPaths...); err != nil { + return errors.Wrap(err, "failed to validate .spec.customReplicationTLSSecret") + } + return nil +} + +func (r *PGClusterReconciler) reconcileOldCACert(ctx context.Context, cr *v2.PerconaPGCluster) error { + if cr.Spec.Secrets.CustomRootCATLSSecret != nil { + return nil + } + + oldCASecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: naming.RootCertSecret, + Namespace: cr.Namespace, + }, + } + err := r.Client.Get(ctx, client.ObjectKeyFromObject(oldCASecret), oldCASecret) + if client.IgnoreNotFound(err) != nil { + return errors.Wrap(err, "failed to get old ca secret") + } + + if cr.CompareVersion("2.5.0") < 0 { + if k8serrors.IsNotFound(err) { + // K8SPG-555: We should create an empty secret with old name, so that crunchy part can populate it + // instead of creating secrets unique to the cluster + // TODO: remove when 2.4.0 will become unsupported + if err := r.Client.Create(ctx, oldCASecret); err != nil { + return errors.Wrap(err, "failed to create ca secret") + } + } + return nil + } + if k8serrors.IsNotFound(err) { + return nil + } + + // K8SPG-555: Previously we used a single CA secret for all clusters in a namespace. + // We should copy the contents of the old CA secret, if it exists, to the new one, which is unique for each cluster. + // TODO: remove when 2.4.0 will become unsupported + newCASecret := &corev1.Secret{ + ObjectMeta: naming.PostgresRootCASecret( + &v1beta1.PostgresCluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: cr.Name, + Namespace: cr.Namespace, + }, + }), + } + err = r.Client.Get(ctx, client.ObjectKeyFromObject(newCASecret), new(corev1.Secret)) + if client.IgnoreNotFound(err) != nil { + return errors.Wrap(err, "failed to get new ca secret") + } + + if k8serrors.IsNotFound(err) { + err := r.Client.Get(ctx, types.NamespacedName{ + Name: cr.Name, + Namespace: cr.Namespace, + }, new(v1beta1.PostgresCluster)) + if client.IgnoreNotFound(err) != nil { + return errors.Wrap(err, "failed to get crunchy cluster") + } + // If the cluster is new, we should not copy the old CA secret. + // We should create an empty secret instead, so that crunchy part can populate it. + if !k8serrors.IsNotFound(err) { + newCASecret.Data = oldCASecret.Data + } + if err := r.Client.Create(ctx, newCASecret); err != nil { + return errors.Wrap(err, "failed to create updated CA secret") + } + } + return nil +} + func (r *PGClusterReconciler) reconcilePMM(ctx context.Context, cr *v2.PerconaPGCluster) error { if !cr.PMMEnabled() { return nil diff --git a/percona/controller/pgcluster/controller_test.go b/percona/controller/pgcluster/controller_test.go index 2595ba948f..e6b3770b71 100644 --- a/percona/controller/pgcluster/controller_test.go +++ b/percona/controller/pgcluster/controller_test.go @@ -5,9 +5,9 @@ package pgcluster import ( "context" - // #nosec G501 - "crypto/md5" + "crypto/md5" //nolint:gosec "fmt" + "strconv" "sync" "time" @@ -767,7 +767,6 @@ var _ = Describe("Users", Ordered, func() { return errors.New("cluster not deleted") }, time.Second*15, time.Millisecond*250).Should(BeNil()) - }) }) @@ -1284,3 +1283,260 @@ var _ = Describe("Operator-created sidecar container resources", Ordered, func() } }) }) + +var _ = Describe("Validate TLS", Ordered, func() { + ctx := context.Background() + + const crName = "validate-tls" + const ns = crName + + namespace := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: crName, + Namespace: ns, + }, + } + + BeforeAll(func() { + By("Creating the Namespace to perform the tests") + err := k8sClient.Create(ctx, namespace) + Expect(err).To(Not(HaveOccurred())) + }) + + AfterAll(func() { + By("Deleting the Namespace to perform the tests") + _ = k8sClient.Delete(ctx, namespace) + }) + + cr, err := readDefaultCR(crName, ns) + It("should read default cr.yaml", func() { + Expect(err).NotTo(HaveOccurred()) + }) + + cr.Default() + + It("should create PerconaPGCluster", func() { + Expect(k8sClient.Create(ctx, cr)).Should(Succeed()) + }) + + checkSecretProjection := func(cr *v2.PerconaPGCluster, projection *corev1.SecretProjection, secretName string, neededKeys []string) { + GinkgoHelper() + It("should fail if secret doesn't exist", func() { + projection.Name = secretName + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).To(HaveOccurred()) + }) + It("should fail if secret doesn't have needed data", func() { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: cr.Namespace, + }, + } + Expect(k8sClient.Create(ctx, secret)).NotTo(HaveOccurred()) + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).To(HaveOccurred()) + }) + + It("should not fail if needed keys specified in the secret", func() { + secret := new(corev1.Secret) + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Name: secretName, + Namespace: cr.Namespace, + }, secret)).NotTo(HaveOccurred()) + secret.Data = make(map[string][]byte) + for _, v := range neededKeys { + secret.Data[v] = []byte("some-data") + } + Expect(k8sClient.Update(ctx, secret)).NotTo(HaveOccurred()) + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should not fail if wrong items.path are specified but needed key exist in secrets", func() { + projection.Items = []corev1.KeyToPath{} + for i, v := range neededKeys { + projection.Items = append(projection.Items, corev1.KeyToPath{ + Key: v, + Path: "wrong-path" + "-" + strconv.Itoa(i), + }) + } + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should fail if items.key are specified which don't exist in the secret", func() { + projection.Items = []corev1.KeyToPath{} + for i, v := range neededKeys { + projection.Items = append(projection.Items, corev1.KeyToPath{ + Key: "non-existent-key" + strconv.Itoa(i), + Path: v, + }) + } + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).To(HaveOccurred()) + }) + + It("should not fail if wrong items.path are specified but needed key exist in secrets", func() { + projection.Items = []corev1.KeyToPath{} + for _, v := range neededKeys { + projection.Items = append(projection.Items, corev1.KeyToPath{ + Key: v, + Path: v + "-wrong", + }) + } + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should not fail with custom data keys in the secret", func() { + secret := new(corev1.Secret) + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Name: secretName, + Namespace: cr.Namespace, + }, secret)).NotTo(HaveOccurred()) + + secret.Data = map[string][]byte{} + for _, v := range neededKeys { + secret.Data[v+"-custom"] = []byte("some-data") + } + Expect(k8sClient.Update(ctx, secret)).NotTo(HaveOccurred()) + projection.Items = []corev1.KeyToPath{} + for _, v := range neededKeys { + projection.Items = append(projection.Items, corev1.KeyToPath{ + Key: v + "-custom", + Path: v, + }) + } + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should fail if items.key are specified but not paths", func() { + projection.Items = []corev1.KeyToPath{} + for _, v := range neededKeys { + projection.Items = append(projection.Items, corev1.KeyToPath{ + Key: v, + }) + } + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).To(HaveOccurred()) + }) + } + + Context("checking validation", func() { + Describe("should check validation for cr.Spec.Secrets.CustomRootCATLSSecret", func() { + cr := cr.DeepCopy() + neededKeys := []string{ + "root.crt", + "root.key", + } + cr.Spec.Secrets.CustomRootCATLSSecret = new(corev1.SecretProjection) + checkSecretProjection(cr, cr.Spec.Secrets.CustomRootCATLSSecret, "root-ca", neededKeys) + It("should not fail if the section was not specified", func() { + cr.Spec.Secrets.CustomRootCATLSSecret = nil + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + }) + Describe("should check validation for cr.Spec.Secrets.CustomTLSSecret", func() { + cr := cr.DeepCopy() + neededKeys := []string{ + "ca.crt", + "tls.crt", + "tls.key", + } + cr.Spec.Secrets.CustomTLSSecret = new(corev1.SecretProjection) + checkSecretProjection(cr, cr.Spec.Secrets.CustomTLSSecret, "tls-secret", neededKeys) + It("should not fail if the section was not specified", func() { + cr.Spec.Secrets.CustomTLSSecret = nil + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + }) + Describe("should check validation for cr.Spec.Secrets.CustomReplicationClientTLSSecret", func() { + cr := cr.DeepCopy() + neededKeys := []string{ + "ca.crt", + "tls.crt", + "tls.key", + } + cr.Spec.Secrets.CustomReplicationClientTLSSecret = new(corev1.SecretProjection) + checkSecretProjection(cr, cr.Spec.Secrets.CustomReplicationClientTLSSecret, "repl-tls-secret", neededKeys) + It("should not fail if the section was not specified", func() { + cr.Spec.Secrets.CustomReplicationClientTLSSecret = nil + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) + + checkSecretProjectionWithCA := func(cr *v2.PerconaPGCluster, projection *corev1.SecretProjection, secretName string) { + GinkgoHelper() + neededKeys := []string{ + "tls.crt", + "tls.key", + } + projection.Name = secretName + It("should create secret", func() { + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: cr.Namespace, + }, + } + secret.Data = map[string][]byte{} + for _, v := range neededKeys { + secret.Data[v] = []byte("some-data") + } + Expect(k8sClient.Create(ctx, secret)).NotTo(HaveOccurred()) + }) + It("should fail when CA is not specified", func() { + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).To(HaveOccurred()) + }) + It("should not fail when CA is specified", func() { + secretName := secretName + "-ca" + neededKeys := []string{ + "root.crt", + "root.key", + } + cr.Spec.Secrets.CustomRootCATLSSecret = new(corev1.SecretProjection) + projection := cr.Spec.Secrets.CustomRootCATLSSecret + projection.Name = secretName + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretName, + Namespace: cr.Namespace, + }, + } + secret.Data = make(map[string][]byte) + for _, v := range neededKeys { + secret.Data[v] = []byte("some-data") + } + Expect(k8sClient.Create(ctx, secret)).NotTo(HaveOccurred()) + + err := reconciler(cr).validateTLS(ctx, cr) + Expect(err).NotTo(HaveOccurred()) + }) + } + Context("check validation for cr.Spec.Secrets.CustomTLSSecret when cr.Spec.Secrets.CustomRootCATLSSecret is specified", func() { + cr := cr.DeepCopy() + secretName := "custom-tls-secret-with-ca" //nolint:gosec + cr.Spec.Secrets.CustomTLSSecret = new(corev1.SecretProjection) + checkSecretProjectionWithCA(cr, cr.Spec.Secrets.CustomTLSSecret, secretName) + }) + Context("should check validation for cr.Spec.Secrets.CustomReplicationClientTLSSecret when cr.Spec.Secrets.CustomRootCATLSSecret is specified", func() { + cr := cr.DeepCopy() + secretName := "custom-replication-tls-secret-with-ca" //nolint:gosec + cr.Spec.Secrets.CustomReplicationClientTLSSecret = new(corev1.SecretProjection) + checkSecretProjectionWithCA(cr, cr.Spec.Secrets.CustomReplicationClientTLSSecret, secretName) + }) +}) diff --git a/percona/controller/pgcluster/finalizer.go b/percona/controller/pgcluster/finalizer.go index f423acb65a..4e7ca47a16 100644 --- a/percona/controller/pgcluster/finalizer.go +++ b/percona/controller/pgcluster/finalizer.go @@ -82,6 +82,7 @@ func (r *PGClusterReconciler) deleteTLSSecrets(ctx context.Context, cr *v2.Perco naming.ClusterPGBouncer(crunchyCluster), } if cr.Spec.Secrets.CustomRootCATLSSecret == nil { + secretsMeta = append(secretsMeta, metav1.ObjectMeta{Namespace: crunchyCluster.Namespace, Name: naming.RootCertSecret}) secretsMeta = append(secretsMeta, naming.PostgresRootCASecret(crunchyCluster)) } if cr.Spec.Secrets.CustomTLSSecret == nil { diff --git a/percona/controller/testdata/sidecar-resources-cr.yaml b/percona/controller/testdata/sidecar-resources-cr.yaml index 591c95f712..2a363cb7ca 100644 --- a/percona/controller/testdata/sidecar-resources-cr.yaml +++ b/percona/controller/testdata/sidecar-resources-cr.yaml @@ -4,7 +4,7 @@ kind: PerconaPGCluster metadata: name: cluster1 spec: - crVersion: 2.4.0 + crVersion: 2.5.0 image: perconalab/percona-postgresql-operator:main-ppg16-postgres imagePullPolicy: Always diff --git a/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go b/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go index 58939e0e75..b7f063ebf6 100644 --- a/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go +++ b/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go @@ -5,7 +5,6 @@ import ( "os" gover "github.com/hashicorp/go-version" - v "github.com/hashicorp/go-version" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -22,7 +21,7 @@ func init() { } const ( - Version = "2.5.0" + Version = "2.6.0" ProductName = "pg-operator" ) @@ -334,12 +333,12 @@ func (cr *PerconaPGCluster) ToCrunchy(ctx context.Context, postgresCluster *crun return postgresCluster, nil } -func (cr *PerconaPGCluster) Version() *v.Version { - return v.Must(v.NewVersion(cr.Spec.CRVersion)) +func (cr *PerconaPGCluster) Version() *gover.Version { + return gover.Must(gover.NewVersion(cr.Spec.CRVersion)) } func (cr *PerconaPGCluster) CompareVersion(ver string) int { - return cr.Version().Compare(v.Must(v.NewVersion(ver))) + return cr.Version().Compare(gover.Must(gover.NewVersion(ver))) } type AppState string diff --git a/pkg/apis/postgres-operator.crunchydata.com/v1beta1/postgrescluster_types.go b/pkg/apis/postgres-operator.crunchydata.com/v1beta1/postgrescluster_types.go index 4dcf64ac5e..39d903e47d 100644 --- a/pkg/apis/postgres-operator.crunchydata.com/v1beta1/postgrescluster_types.go +++ b/pkg/apis/postgres-operator.crunchydata.com/v1beta1/postgrescluster_types.go @@ -18,6 +18,7 @@ package v1beta1 import ( "fmt" + gover "github.com/hashicorp/go-version" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/intstr" @@ -733,3 +734,13 @@ func NewPostgresCluster() *PostgresCluster { cluster.SetGroupVersionKind(GroupVersion.WithKind("PostgresCluster")) return cluster } + +const LabelVersion = "pgv2.percona.com/version" + +func (cr *PostgresCluster) CompareVersion(ver string) int { + crVersion, err := gover.NewVersion(cr.Labels[LabelVersion]) + if err != nil { + return -2 + } + return crVersion.Compare(gover.Must(gover.NewVersion(ver))) +}