diff --git a/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml b/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml index fd4317efe5..91966db202 100644 --- a/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml +++ b/build/crd/percona/generated/pgv2.percona.com_perconapgclusters.yaml @@ -8340,8 +8340,12 @@ spec: properties: bucket: type: string + disableSSL: + type: string endpoint: type: string + forcePathStyle: + type: string region: type: string secret: diff --git a/build/postgres-operator/install-extensions.sh b/build/postgres-operator/install-extensions.sh index 6a8555bd78..ca09efe7cd 100755 --- a/build/postgres-operator/install-extensions.sh +++ b/build/postgres-operator/install-extensions.sh @@ -16,6 +16,14 @@ if [[ -n $STORAGE_ENDPOINT ]]; then args+=(-endpoint "$STORAGE_ENDPOINT") fi +if [[ ${STORAGE_DISABLE_SSL} == "true" ]]; then + args+=(-disable-ssl) +fi + +if [[ ${STORAGE_FORCE_PATH_STYLE} == "true" ]]; then + args+=(-force-path-style) +fi + for key in "${extensions[@]}"; do if [ -f "${PGDATA_EXTENSIONS}"/"${key}".installed ]; then echo "Extension ${key} already installed" diff --git a/cmd/extension-installer/main.go b/cmd/extension-installer/main.go index 5045e608eb..4275e49b3e 100644 --- a/cmd/extension-installer/main.go +++ b/cmd/extension-installer/main.go @@ -12,7 +12,7 @@ import ( func main() { var storageType, endpoint, region, bucket, key, extensionPath string - var install, uninstall bool + var install, uninstall, forcePathStyle, disableSSL bool flag.StringVar(&storageType, "type", "", "Storage type") flag.StringVar(&endpoint, "endpoint", "", "Storage endpoint") @@ -23,6 +23,8 @@ func main() { flag.BoolVar(&install, "install", false, "Install extension") flag.BoolVar(&uninstall, "uninstall", false, "Uninstall extension") + flag.BoolVar(&forcePathStyle, "force-path-style", false, "Force path style") + flag.BoolVar(&disableSSL, "disable-ssl", false, "Disable SSL") flag.Parse() if (install && uninstall) || (!install && !uninstall) { @@ -31,7 +33,7 @@ func main() { log.Printf("starting extension installer for %s/%s (%s) in %s", bucket, key, storageType, region) - storage := initStorage(extensions.StorageType(storageType), endpoint, bucket, region) + storage := initStorage(extensions.StorageType(storageType), endpoint, bucket, region, forcePathStyle, disableSSL) packageName := key + ".tar.gz" @@ -70,10 +72,10 @@ func main() { } } -func initStorage(storageType extensions.StorageType, endpoint, bucket, region string) extensions.ObjectGetter { +func initStorage(storageType extensions.StorageType, endpoint, bucket, region string, s3ForcePathStyle, disableSSL bool) extensions.ObjectGetter { switch storageType { case extensions.StorageTypeS3: - return extensions.NewS3(endpoint, region, bucket) + return extensions.NewS3(endpoint, region, bucket, s3ForcePathStyle, disableSSL) default: log.Fatalf("unknown storage type: %s", os.Getenv("STORAGE_TYPE")) } diff --git a/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml b/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml index a0dccc91bf..882ca6883d 100644 --- a/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml +++ b/config/crd/bases/pgv2.percona.com_perconapgclusters.yaml @@ -8747,8 +8747,12 @@ spec: properties: bucket: type: string + disableSSL: + type: string endpoint: type: string + forcePathStyle: + type: string region: type: string secret: diff --git a/deploy/bundle.yaml b/deploy/bundle.yaml index 5ec0a7101f..ae21792b14 100644 --- a/deploy/bundle.yaml +++ b/deploy/bundle.yaml @@ -9044,8 +9044,12 @@ spec: properties: bucket: type: string + disableSSL: + type: string endpoint: type: string + forcePathStyle: + type: string region: type: string secret: diff --git a/deploy/cr.yaml b/deploy/cr.yaml index 5f4cffe8b3..a02a23da84 100644 --- a/deploy/cr.yaml +++ b/deploy/cr.yaml @@ -629,6 +629,8 @@ spec: # bucket: pg-extensions # region: eu-central-1 # endpoint: s3.eu-central-1.amazonaws.com +# forcePathStyle: false +# disableSSL: false # secret: # name: cluster1-extensions-secret # builtin: diff --git a/deploy/crd.yaml b/deploy/crd.yaml index 407ac4d2d9..9127130294 100644 --- a/deploy/crd.yaml +++ b/deploy/crd.yaml @@ -9044,8 +9044,12 @@ spec: properties: bucket: type: string + disableSSL: + type: string endpoint: type: string + forcePathStyle: + type: string region: type: string secret: diff --git a/deploy/cw-bundle.yaml b/deploy/cw-bundle.yaml index 0e0dc91efb..149df41fff 100644 --- a/deploy/cw-bundle.yaml +++ b/deploy/cw-bundle.yaml @@ -9044,8 +9044,12 @@ spec: properties: bucket: type: string + disableSSL: + type: string endpoint: type: string + forcePathStyle: + type: string region: type: string secret: diff --git a/e2e-tests/conf/minio-secret.yml b/e2e-tests/conf/minio-secret.yml new file mode 100644 index 0000000000..1c44db1c1f --- /dev/null +++ b/e2e-tests/conf/minio-secret.yml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: Secret +metadata: + name: minio-secret +type: Opaque +data: + AWS_ACCESS_KEY_ID: c29tZS1hY2Nlc3Mta2V5 + AWS_SECRET_ACCESS_KEY: c29tZS1zZWNyZXQta2V5 diff --git a/e2e-tests/functions b/e2e-tests/functions index 0a28f8fe0e..887ef99dae 100644 --- a/e2e-tests/functions +++ b/e2e-tests/functions @@ -125,6 +125,56 @@ get_operator_pod() { echo $(kubectl get pods -n "${OPERATOR_NS:-$NAMESPACE}" --selector=app.kubernetes.io/name=percona-postgresql-operator -o jsonpath='{.items[].metadata.name}') } +retry() { + local max=$1 + local delay=$2 + shift 2 # cut delay and max args + local n=1 + + until "$@"; do + if [[ $n -ge $max ]]; then + echo "The command ${*} has failed after $n attempts." + exit 1 + fi + ((n++)) + sleep $delay + done +} + +deploy_minio() { + local access_key + local secret_key + access_key="$(kubectl -n "${NAMESPACE}" get secret minio-secret -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 -d)" + secret_key="$(kubectl -n "${NAMESPACE}" get secret minio-secret -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 -d)" + + helm uninstall -n "${NAMESPACE}" minio-service || : + helm repo remove minio || : + helm repo add minio https://charts.min.io/ + retry 10 60 helm install minio-service \ + -n "${NAMESPACE}" \ + --version "${MINIO_VER}" \ + --set replicas=1 \ + --set mode=standalone \ + --set resources.requests.memory=256Mi \ + --set rootUser=rootuser \ + --set rootPassword=rootpass123 \ + --set "users[0].accessKey"="$(printf '%q' "$(printf '%q' "$access_key")")" \ + --set "users[0].secretKey"="$(printf '%q' "$(printf '%q' "$secret_key")")" \ + --set "users[0].policy"=consoleAdmin \ + --set service.type=ClusterIP \ + --set configPathmc=/tmp/.minio/ \ + --set persistence.size=2G \ + --set securityContext.enabled=false \ + minio/minio + MINIO_POD=$(kubectl -n "${NAMESPACE}" get pods --selector=release=minio-service -o 'jsonpath={.items[].metadata.name}') + wait_pod $MINIO_POD + + # create bucket + kubectl -n "${NAMESPACE}" run -i --rm aws-cli --image=perconalab/awscli --restart=Never -- \ + bash -c "AWS_ACCESS_KEY_ID='$access_key' AWS_SECRET_ACCESS_KEY='$secret_key' AWS_DEFAULT_REGION=us-east-1 \ + /usr/bin/aws --endpoint-url http://minio-service:9000 s3 mb s3://operator-testing" +} + deploy_s3_secrets() { set +o xtrace printf "[global]\nrepo1-s3-key=%s\nrepo1-s3-key-secret=%s\n" \ @@ -147,6 +197,7 @@ deploy_s3_secrets() { ;; "custom-extensions" | "major-upgrade") kubectl -n "${NAMESPACE}" apply -f "${TESTS_CONFIG_DIR}/cloud-secret.yml" + kubectl -n "${NAMESPACE}" apply -f "${TESTS_CONFIG_DIR}/minio-secret.yml" ;; *) kubectl -n "${NAMESPACE}" create secret generic "${test_name}-pgbackrest-secrets" --from-file=cloud.conf="${TEMP_DIR}/pgbackrest-secret.ini" @@ -229,13 +280,20 @@ get_cr() { .spec.backups.pgbackrest.repos += [{"name":"repo3","azure":{"container":"'$BUCKET'"}}] ' $TEMP_DIR/cr.yaml ;; - "custom-extensions" | "major-upgrade") + "major-upgrade") yq eval -i ' .spec.extensions.image = "'$IMAGE'" | .spec.extensions.imagePullPolicy = "Always" | .spec.extensions.storage = {"type": "s3", "bucket": "pg-extensions", "region": "eu-central-1", "secret": {"name": "aws-s3-secret"}} ' $TEMP_DIR/cr.yaml ;; + "custom-extensions") + yq eval -i ' + .spec.extensions.image = "'$IMAGE'" | + .spec.extensions.imagePullPolicy = "Always" | + .spec.extensions.storage = {"type": "s3", "bucket": "operator-testing", "region": "us-east-1", "endpoint": "http://minio-service:9000", "forcePathStyle": "true", "disableSSL": "true", "secret": {"name": "minio-secret"}} + ' $TEMP_DIR/cr.yaml + ;; esac cat $TEMP_DIR/cr.yaml } @@ -276,12 +334,52 @@ get_psql_user_host() { kubectl -n ${NAMESPACE} get "secret/${secret_name}" --template='{{.data.host | base64decode }}' } +get_aws_access_key() { + local secret_name=${1} + + kubectl -n ${NAMESPACE} get "secret/${secret_name}" --template='{{.data.AWS_SECRET_ACCESS_KEY | base64decode }}' +} + +get_aws_access_key_id() { + local secret_name=${1} + + kubectl -n ${NAMESPACE} get "secret/${secret_name}" --template='{{.data.AWS_ACCESS_KEY_ID | base64decode }}' +} + +get_psql_user_host() { + local secret_name=${1} + + kubectl -n ${NAMESPACE} get "secret/${secret_name}" --template='{{.data.host | base64decode }}' +} get_instance_set_pods() { local instance=${1:-instance1} kubectl get pods -n ${NAMESPACE} --selector postgres-operator.crunchydata.com/instance-set=${instance} -o custom-columns='NAME:.metadata.name' --no-headers } +copy_custom_extensions_form_aws() { + set +o xtrace + + access_key="$(kubectl -n "${NAMESPACE}" get secret minio-secret -o jsonpath='{.data.AWS_ACCESS_KEY_ID}' | base64 -d)" + secret_key="$(kubectl -n "${NAMESPACE}" get secret minio-secret -o jsonpath='{.data.AWS_SECRET_ACCESS_KEY}' | base64 -d)" + + kubectl -n "${NAMESPACE}" run -i --rm aws-cli \ + --image=perconalab/awscli \ + --restart=Never -- \ + bash -c " + AWS_ACCESS_KEY_ID=$(get_aws_access_key_id aws-s3-secret) \ + AWS_SECRET_ACCESS_KEY=$(get_aws_access_key aws-s3-secret) \ + AWS_DEFAULT_REGION=eu-central-1 \ + /usr/bin/aws --endpoint-url https://s3.amazonaws.com s3 cp s3://pg-extensions/ /tmp/ --recursive && + + AWS_ACCESS_KEY_ID='${access_key}' \ + AWS_SECRET_ACCESS_KEY='${secret_key}' \ + AWS_DEFAULT_REGION=us-east-1 \ + /usr/bin/aws --endpoint-url http://minio-service:9000 s3 cp /tmp/ s3://operator-testing/ --recursive + " + set -o xtrace +} + get_psql_pod_host() { local pod=${1} diff --git a/e2e-tests/tests/custom-extensions/00-deploy-operator.yaml b/e2e-tests/tests/custom-extensions/00-deploy-operator.yaml index 7faf4da852..0cfe9bbd0e 100644 --- a/e2e-tests/tests/custom-extensions/00-deploy-operator.yaml +++ b/e2e-tests/tests/custom-extensions/00-deploy-operator.yaml @@ -12,3 +12,5 @@ commands: deploy_operator deploy_client deploy_s3_secrets + deploy_minio + copy_custom_extensions_form_aws diff --git a/e2e-tests/tests/custom-extensions/02-assert.yaml b/e2e-tests/tests/custom-extensions/02-assert.yaml index 4a86a66c94..ae1514543e 100644 --- a/e2e-tests/tests/custom-extensions/02-assert.yaml +++ b/e2e-tests/tests/custom-extensions/02-assert.yaml @@ -35,15 +35,19 @@ spec: value: s3 - name: STORAGE_ENDPOINT - name: STORAGE_REGION - value: eu-central-1 + value: us-east-1 - name: STORAGE_BUCKET - value: pg-extensions + value: operator-testing - name: INSTALL_EXTENSIONS - name: PG_VERSION - name: PGDATA_EXTENSIONS + - name: STORAGE_DISABLE_SSL + value: "true" + - name: STORAGE_FORCE_PATH_STYLE + value: "true" envFrom: - secretRef: - name: aws-s3-secret + name: minio-secret resources: {} volumeMounts: - mountPath: /pgdata diff --git a/e2e-tests/tests/custom-extensions/02-install-pg_cron.yaml b/e2e-tests/tests/custom-extensions/02-install-pg_cron.yaml index dbe9380235..7d51929101 100644 --- a/e2e-tests/tests/custom-extensions/02-install-pg_cron.yaml +++ b/e2e-tests/tests/custom-extensions/02-install-pg_cron.yaml @@ -11,4 +11,4 @@ spec: dynamicConfiguration: postgresql: parameters: - shared_preload_libraries: pg_cron \ No newline at end of file + shared_preload_libraries: pg_cron diff --git a/e2e-tests/tests/custom-extensions/05-assert.yaml b/e2e-tests/tests/custom-extensions/05-assert.yaml index 4a86a66c94..ae1514543e 100644 --- a/e2e-tests/tests/custom-extensions/05-assert.yaml +++ b/e2e-tests/tests/custom-extensions/05-assert.yaml @@ -35,15 +35,19 @@ spec: value: s3 - name: STORAGE_ENDPOINT - name: STORAGE_REGION - value: eu-central-1 + value: us-east-1 - name: STORAGE_BUCKET - value: pg-extensions + value: operator-testing - name: INSTALL_EXTENSIONS - name: PG_VERSION - name: PGDATA_EXTENSIONS + - name: STORAGE_DISABLE_SSL + value: "true" + - name: STORAGE_FORCE_PATH_STYLE + value: "true" envFrom: - secretRef: - name: aws-s3-secret + name: minio-secret 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 c8b4226623..f0c57e2972 100644 --- a/e2e-tests/tests/custom-extensions/06-assert.yaml +++ b/e2e-tests/tests/custom-extensions/06-assert.yaml @@ -37,15 +37,19 @@ spec: value: s3 - name: STORAGE_ENDPOINT - name: STORAGE_REGION - value: eu-central-1 + value: us-east-1 - name: STORAGE_BUCKET - value: pg-extensions + value: operator-testing - name: INSTALL_EXTENSIONS - name: PG_VERSION - name: PGDATA_EXTENSIONS + - name: STORAGE_DISABLE_SSL + value: "true" + - name: STORAGE_FORCE_PATH_STYLE + value: "true" envFrom: - secretRef: - name: aws-s3-secret + name: minio-secret resources: {} volumeMounts: - mountPath: /pgdata diff --git a/e2e-tests/tests/custom-extensions/10-assert.yaml b/e2e-tests/tests/custom-extensions/10-assert.yaml new file mode 100644 index 0000000000..6c5c6eb780 --- /dev/null +++ b/e2e-tests/tests/custom-extensions/10-assert.yaml @@ -0,0 +1,134 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 180 +--- +kind: StatefulSet +apiVersion: apps/v1 +metadata: + name: custom-extensions-repo-host + labels: + postgres-operator.crunchydata.com/cluster: custom-extensions + 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: custom-extensions + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 1 + replicas: 1 + readyReplicas: 1 + currentReplicas: 1 + updatedReplicas: 1 + collisionCount: 0 +--- +kind: StatefulSet +apiVersion: apps/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: custom-extensions + postgres-operator.crunchydata.com/data: postgres + postgres-operator.crunchydata.com/instance-set: instance1 + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: custom-extensions + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 4 + replicas: 1 + readyReplicas: 1 + updatedReplicas: 1 + collisionCount: 0 +--- +kind: Deployment +apiVersion: apps/v1 +metadata: + name: custom-extensions-pgbouncer + labels: + postgres-operator.crunchydata.com/cluster: custom-extensions + postgres-operator.crunchydata.com/role: pgbouncer + annotations: + deployment.kubernetes.io/revision: '1' + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: custom-extensions + controller: true + blockOwnerDeletion: true +status: + observedGeneration: 1 + replicas: 3 + updatedReplicas: 3 + readyReplicas: 3 +--- +kind: Job +apiVersion: batch/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: custom-extensions + 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: custom-extensions + ownerReferences: + - apiVersion: pgv2.percona.com/v2 + kind: PerconaPGCluster + name: custom-extensions + controller: true + blockOwnerDeletion: true + finalizers: + - postgres-operator.crunchydata.com/finalizer +status: + instances: + - name: instance1 + readyReplicas: 3 + replicas: 3 + updatedReplicas: 3 + observedGeneration: 4 + 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: custom-extensions +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/custom-extensions/10-use-aws-storage.yaml b/e2e-tests/tests/custom-extensions/10-use-aws-storage.yaml new file mode 100644 index 0000000000..f350f7c6c1 --- /dev/null +++ b/e2e-tests/tests/custom-extensions/10-use-aws-storage.yaml @@ -0,0 +1,20 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +timeout: 10 +commands: + - script: |- + set -o errexit + set -o xtrace + + source ../../functions + kubectl patch pg custom-extensions \ + -n "${NAMESPACE}" \ + --type='json' \ + -p='[ + {"op": "remove", "path": "/spec/extensions/storage/disableSSL"}, + {"op": "remove", "path": "/spec/extensions/storage/forcePathStyle"}, + {"op": "replace", "path": "/spec/extensions/storage/secret/name", "value": "aws-s3-secret"}, + {"op": "replace", "path": "/spec/extensions/storage/region", "value": "eu-central-1"}, + {"op": "replace", "path": "/spec/extensions/storage/bucket", "value": "pg-extensions"}, + {"op": "replace", "path": "/spec/extensions/storage/endpoint", "value": "s3.eu-central-1.amazonaws.com"} + ]' diff --git a/e2e-tests/tests/custom-extensions/11-assert.yaml b/e2e-tests/tests/custom-extensions/11-assert.yaml new file mode 100644 index 0000000000..9e347dd025 --- /dev/null +++ b/e2e-tests/tests/custom-extensions/11-assert.yaml @@ -0,0 +1,80 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 120 +--- +kind: StatefulSet +apiVersion: apps/v1 +metadata: + labels: + postgres-operator.crunchydata.com/cluster: custom-extensions + postgres-operator.crunchydata.com/data: postgres + postgres-operator.crunchydata.com/instance-set: instance1 + ownerReferences: + - apiVersion: postgres-operator.crunchydata.com/v1beta1 + kind: PostgresCluster + name: custom-extensions + controller: true + blockOwnerDeletion: true +spec: + template: + spec: + initContainers: + - name: postgres-startup + - command: + - /usr/local/bin/relocate-extensions.sh + resources: {} + volumeMounts: + - mountPath: /pgdata + name: postgres-data + - mountPath: /tmp + name: tmp + - command: + - /usr/local/bin/install-extensions.sh + env: + - name: STORAGE_TYPE + value: s3 + - name: STORAGE_ENDPOINT + - name: STORAGE_REGION + value: eu-central-1 + - name: STORAGE_BUCKET + value: pg-extensions + - name: INSTALL_EXTENSIONS + - name: PG_VERSION + - name: PGDATA_EXTENSIONS + envFrom: + - secretRef: + name: aws-s3-secret + resources: {} + volumeMounts: + - mountPath: /pgdata + name: postgres-data + - name: postgres-data + - name: postgres-data + - mountPath: /tmp + name: tmp + - command: + - /usr/local/bin/init-entrypoint.sh + - name: nss-wrapper-init +status: + observedGeneration: 5 + replicas: 1 + updatedReplicas: 1 + readyReplicas: 1 + availableReplicas: 1 +--- +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + name: custom-extensions +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/custom-extensions/11-install-pg_cron.yaml b/e2e-tests/tests/custom-extensions/11-install-pg_cron.yaml new file mode 100644 index 0000000000..7d51929101 --- /dev/null +++ b/e2e-tests/tests/custom-extensions/11-install-pg_cron.yaml @@ -0,0 +1,14 @@ +apiVersion: pgv2.percona.com/v2 +kind: PerconaPGCluster +metadata: + name: custom-extensions +spec: + extensions: + custom: + - name: pg_cron + version: 1.6.1 + patroni: + dynamicConfiguration: + postgresql: + parameters: + shared_preload_libraries: pg_cron diff --git a/e2e-tests/tests/custom-extensions/12-enable-pg_cron.yaml b/e2e-tests/tests/custom-extensions/12-enable-pg_cron.yaml new file mode 100644 index 0000000000..b0800ef10a --- /dev/null +++ b/e2e-tests/tests/custom-extensions/12-enable-pg_cron.yaml @@ -0,0 +1,13 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +commands: + - script: |- + set -o errexit + set -o xtrace + + source ../../functions + + wait_cluster_consistency custom-extensions + + run_psql_local '\c postgres \\\ CREATE EXTENSION pg_cron' "postgres:$(get_psql_user_pass custom-extensions-pguser-postgres)@$(get_psql_user_host custom-extensions-pguser-postgres)" + timeout: 360 diff --git a/e2e-tests/tests/custom-extensions/13-assert.yaml b/e2e-tests/tests/custom-extensions/13-assert.yaml new file mode 100644 index 0000000000..c56ea2c1fb --- /dev/null +++ b/e2e-tests/tests/custom-extensions/13-assert.yaml @@ -0,0 +1,13 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestAssert +timeout: 30 +--- +kind: ConfigMap +apiVersion: v1 +metadata: + name: 13-check-extensions +data: + data: |2- + pg_cron + pg_stat_monitor + pgaudit diff --git a/e2e-tests/tests/custom-extensions/13-check-extensions.yaml b/e2e-tests/tests/custom-extensions/13-check-extensions.yaml new file mode 100644 index 0000000000..c187cfd854 --- /dev/null +++ b/e2e-tests/tests/custom-extensions/13-check-extensions.yaml @@ -0,0 +1,13 @@ +apiVersion: kuttl.dev/v1beta1 +kind: TestStep +timeout: 30 +commands: + - script: |- + set -o errexit + set -o xtrace + + source ../../functions + + data=$(kubectl -n ${NAMESPACE} exec $(get_client_pod) -- psql -v ON_ERROR_STOP=1 -t -q postgres://postgres:$(get_psql_user_pass custom-extensions-pguser-postgres)@$(get_psql_user_host custom-extensions-pguser-postgres) -c "\c postgres" -c "select name from pg_available_extensions where name in ('pg_cron','pg_stat_monitor','pgaudit') order by name") + + kubectl create configmap -n "${NAMESPACE}" 13-check-extensions --from-literal=data="${data}" diff --git a/e2e-tests/vars.sh b/e2e-tests/vars.sh index 47a3dd2cf6..0516b3901c 100755 --- a/e2e-tests/vars.sh +++ b/e2e-tests/vars.sh @@ -27,6 +27,7 @@ export IMAGE_PMM3_CLIENT=${IMAGE_PMM3_CLIENT:-"perconalab/pmm-client:3-dev-lates export IMAGE_PMM3_SERVER=${IMAGE_PMM3_SERVER:-"perconalab/pmm-server:3-dev-latest"} export PGOV1_TAG=${PGOV1_TAG:-"1.4.0"} export PGOV1_VER=${PGOV1_VER:-"14"} +export MINIO_VER="5.4.0" # shellcheck disable=SC2034 date=$(which gdate || which date) diff --git a/percona/controller/pgcluster/controller.go b/percona/controller/pgcluster/controller.go index f3e0d03928..480570a6d1 100644 --- a/percona/controller/pgcluster/controller.go +++ b/percona/controller/pgcluster/controller.go @@ -806,17 +806,17 @@ func (r *PGClusterReconciler) reconcileCustomExtensions(ctx context.Context, cr for i := 0; i < len(cr.Spec.InstanceSets); i++ { set := &cr.Spec.InstanceSets[i] - set.InitContainers = append(set.InitContainers, extensions.ExtensionRelocatorContainer( + set.InitContainers = append(set.InitContainers, extensions.RelocatorContainer( cr, cr.PostgresImage(), cr.Spec.ImagePullPolicy, cr.Spec.PostgresVersion, )) - set.InitContainers = append(set.InitContainers, extensions.ExtensionInstallerContainer( + set.InitContainers = append(set.InitContainers, extensions.InstallerContainer( cr, cr.Spec.PostgresVersion, &cr.Spec.Extensions, strings.Join(extensionKeys, ","), cr.Spec.OpenShift, )) - set.VolumeMounts = append(set.VolumeMounts, extensions.ExtensionVolumeMounts(cr.Spec.PostgresVersion)...) + set.VolumeMounts = append(set.VolumeMounts, extensions.VolumeMounts(cr.Spec.PostgresVersion)...) } return nil } diff --git a/percona/controller/pgupgrade/controller.go b/percona/controller/pgupgrade/controller.go index 4a53ce64d6..1f82b929fc 100644 --- a/percona/controller/pgupgrade/controller.go +++ b/percona/controller/pgupgrade/controller.go @@ -180,11 +180,11 @@ func (r *PGUpgradeReconciler) createPGUpgrade(ctx context.Context, cluster *pgv2 extensionKeys = append(extensionKeys, key) } - pgUpgrade.Spec.InitContainers = append(pgUpgrade.Spec.InitContainers, extensions.ExtensionRelocatorContainer( + pgUpgrade.Spec.InitContainers = append(pgUpgrade.Spec.InitContainers, extensions.RelocatorContainer( cluster, *perconaPGUpgrade.Spec.Image, cluster.Spec.ImagePullPolicy, pgVersion, )) - pgUpgrade.Spec.InitContainers = append(pgUpgrade.Spec.InitContainers, extensions.ExtensionInstallerContainer( + pgUpgrade.Spec.InitContainers = append(pgUpgrade.Spec.InitContainers, extensions.InstallerContainer( cluster, pgVersion, &cluster.Spec.Extensions, @@ -194,7 +194,7 @@ func (r *PGUpgradeReconciler) createPGUpgrade(ctx context.Context, cluster *pgv2 } // we're only adding the volume mounts for target version since current volume mounts are already mounted - pgUpgrade.Spec.VolumeMounts = append(pgUpgrade.Spec.VolumeMounts, extensions.ExtensionVolumeMounts( + pgUpgrade.Spec.VolumeMounts = append(pgUpgrade.Spec.VolumeMounts, extensions.VolumeMounts( perconaPGUpgrade.Spec.ToPostgresVersion)..., ) diff --git a/percona/extensions/k8s.go b/percona/extensions/containers.go similarity index 66% rename from percona/extensions/k8s.go rename to percona/extensions/containers.go index 7cf5839ae5..48f6056d11 100644 --- a/percona/extensions/k8s.go +++ b/percona/extensions/containers.go @@ -13,16 +13,11 @@ func GetExtensionKey(pgMajor int, name, version string) string { return fmt.Sprintf("%s-pg%d-%s", name, pgMajor, version) } -// ExtensionRelocatorContainer returns a container that will relocate extensions from the base image (i.e. pg_stat_monitor, pg_audit) +// RelocatorContainer returns a container that will relocate extensions from the base image (i.e. pg_stat_monitor, pg_audit) // to the data directory so we don't lose them when user adds a custom extension. -func ExtensionRelocatorContainer(cr *pgv2.PerconaPGCluster, image string, imagePullPolicy corev1.PullPolicy, postgresVersion int) corev1.Container { - containerName := "extension-relocator" - if cr.CompareVersion("2.4.0") >= 0 { - containerName = fmt.Sprintf("extension-relocator-%d", postgresVersion) - } - +func RelocatorContainer(_ *pgv2.PerconaPGCluster, image string, imagePullPolicy corev1.PullPolicy, postgresVersion int) corev1.Container { return corev1.Container{ - Name: containerName, + Name: fmt.Sprintf("extension-relocator-%d", postgresVersion), Image: image, ImagePullPolicy: imagePullPolicy, Command: []string{"/usr/local/bin/relocate-extensions.sh"}, @@ -41,22 +36,17 @@ func ExtensionRelocatorContainer(cr *pgv2.PerconaPGCluster, image string, imageP } } -func ExtensionInstallerContainer(cr *pgv2.PerconaPGCluster, postgresVersion int, spec *pgv2.ExtensionsSpec, extensions string, openshift *bool) corev1.Container { +func InstallerContainer(cr *pgv2.PerconaPGCluster, postgresVersion int, spec *pgv2.ExtensionsSpec, extensions string, openshift *bool) corev1.Container { mounts := []corev1.VolumeMount{ { Name: "postgres-data", MountPath: "/pgdata", }, } - mounts = append(mounts, ExtensionVolumeMounts(postgresVersion)...) - - containerName := "extension-installer" - if cr.CompareVersion("2.4.0") >= 0 { - containerName = fmt.Sprintf("extension-installer-%d", postgresVersion) - } + mounts = append(mounts, VolumeMounts(postgresVersion)...) container := corev1.Container{ - Name: containerName, + Name: fmt.Sprintf("extension-installer-%d", postgresVersion), Image: spec.Image, ImagePullPolicy: spec.ImagePullPolicy, Command: []string{"/usr/local/bin/install-extensions.sh"}, @@ -100,6 +90,24 @@ func ExtensionInstallerContainer(cr *pgv2.PerconaPGCluster, postgresVersion int, VolumeMounts: mounts, } + if cr.CompareVersion("2.8.0") >= 0 { + // Check whether the configuration exists so that existing e2e tests + // that do not set these values are not affected. + if spec.Storage.DisableSSL != "" { + container.Env = append(container.Env, corev1.EnvVar{ + Name: "STORAGE_DISABLE_SSL", + Value: spec.Storage.DisableSSL, + }) + } + + if spec.Storage.ForcePathStyle != "" { + container.Env = append(container.Env, corev1.EnvVar{ + Name: "STORAGE_FORCE_PATH_STYLE", + Value: spec.Storage.ForcePathStyle, + }) + } + } + if openshift == nil || !*openshift { container.SecurityContext = &corev1.SecurityContext{ RunAsUser: func() *int64 { @@ -112,7 +120,7 @@ func ExtensionInstallerContainer(cr *pgv2.PerconaPGCluster, postgresVersion int, return container } -func ExtensionVolumeMounts(postgresVersion int) []corev1.VolumeMount { +func VolumeMounts(postgresVersion int) []corev1.VolumeMount { return []corev1.VolumeMount{ { Name: "postgres-data", diff --git a/percona/extensions/s3.go b/percona/extensions/s3.go index 0106a7f393..4c1d4ed376 100644 --- a/percona/extensions/s3.go +++ b/percona/extensions/s3.go @@ -15,8 +15,11 @@ type S3 struct { svc *s3.S3 } -func NewS3(endpoint, region, bucket string) *S3 { - cfg := aws.NewConfig().WithRegion(region) +func NewS3(endpoint, region, bucket string, s3ForcePathStyle, disableSSL bool) *S3 { + cfg := aws.NewConfig(). + WithRegion(region). + WithDisableSSL(disableSSL). + WithS3ForcePathStyle(s3ForcePathStyle) if endpoint != "" { cfg = cfg.WithEndpoint(endpoint) diff --git a/percona/extensions/storage.go b/percona/extensions/storage.go index 190aa3fb53..b3279fdf95 100644 --- a/percona/extensions/storage.go +++ b/percona/extensions/storage.go @@ -12,7 +12,5 @@ type ObjectGetter interface { type StorageType string const ( - StorageTypeS3 StorageType = "s3" - StorageTypeGCS StorageType = "gcs" - StorageTypeAzure StorageType = "azure" + StorageTypeS3 StorageType = "s3" ) diff --git a/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go b/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go index dd579c5d4d..46c937e70e 100644 --- a/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go +++ b/pkg/apis/pgv2.percona.com/v2/perconapgcluster_types.go @@ -628,11 +628,13 @@ type CustomExtensionSpec struct { type CustomExtensionsStorageSpec struct { // +kubebuilder:validation:Enum={s3,gcs,azure} - Type string `json:"type,omitempty"` - Bucket string `json:"bucket,omitempty"` - Region string `json:"region,omitempty"` - Endpoint string `json:"endpoint,omitempty"` - Secret *corev1.SecretProjection `json:"secret,omitempty"` + Type string `json:"type,omitempty"` + Bucket string `json:"bucket,omitempty"` + Region string `json:"region,omitempty"` + Endpoint string `json:"endpoint,omitempty"` + ForcePathStyle string `json:"forcePathStyle,omitempty"` + DisableSSL string `json:"disableSSL,omitempty"` + Secret *corev1.SecretProjection `json:"secret,omitempty"` } type BuiltInExtensionsSpec struct {