From 22e86c4607153e3a6a76811c088036ee799ef491 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 20 Feb 2026 13:46:27 +0500 Subject: [PATCH 01/37] install to aws workflow --- .github/workflows/build.yaml | 4 +- .../workflows/install_pgskipper_to_aws.yaml | 145 ++++++++++++++++++ tests/Dockerfile | 3 +- 3 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/install_pgskipper_to_aws.yaml diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index f4d4118a..fe0aecc7 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -132,6 +132,8 @@ jobs: password: ${{secrets.GITHUB_TOKEN}} - name: Prepare Tag run: echo "TAG_NAME=$(echo ${TAG_NAME} | sed 's@refs/tags/@@;s@refs/heads/@@;s@/@_@g')" >> $GITHUB_ENV + - name: Prepare registry (fork or upstream) + run: echo "GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV - name: Get package IDs for delete id: get-ids-for-delete uses: Netcracker/get-package-ids@84bc8eb8bed50218be76e671b3a24c35a1300979 @@ -149,7 +151,7 @@ jobs: platforms: ${{ needs.prepare.outputs.platforms }} push: ${{ env.PUSH }} build-args: PG_VERSION=${{ matrix.component.pg_version }} - tags: ghcr.io/netcracker/${{ matrix.component.name }}:${{ env.TAG_NAME }} + tags: ghcr.io/${{ env.GITHUB_GROUP }}/${{ matrix.component.name }}:${{ env.TAG_NAME }} provenance: false - uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0 with: diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml new file mode 100644 index 00000000..b86c6a49 --- /dev/null +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -0,0 +1,145 @@ +name: Install Pgskipper to AWS + +on: + workflow_dispatch: + inputs: + environment: + description: "Environment Name" + required: false + default: "dev" + test_scenario: + description: "Test scenario (e.g. basic, patroniBasic, or empty for default)" + required: false + default: "basic" + +jobs: + Install-Pgskipper: + environment: "${{ github.event.inputs.environment }}" + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + + - name: Listing downloaded files + run: | + pwd + ls -R ./ + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + with: + version: 'v1.32.0' + + - name: Setup Helm + uses: azure/setup-helm@v3 + + - name: Verify tools + run: | + kubectl version --client + helm version + aws --version + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ vars.AWS_REGION || 'us-east-1' }} + + - name: Configure kubeconfig for EKS + run: | + aws eks update-kubeconfig --region ${{ vars.AWS_REGION || 'us-east-1' }} --name ${{ vars.AWS_CLUSTERNAME }} + + - name: Verify cluster access + run: kubectl get nodes + + - name: Install Pgskipper (Patroni Services) + run: | + # Uninstall Helm release first (cleans CRs and avoids finalizers blocking namespace deletion) + echo "Uninstalling Helm release..." + helm uninstall pgskipper -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --wait --timeout=120s || true + + echo "Deleting namespace..." + kubectl delete ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=180s --wait=false || true + + echo "Waiting for namespace deletion to complete..." + for i in $(seq 1 90); do + if ! kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null; then + echo "Namespace deleted successfully" + break + fi + + NS_STATUS=$(kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.status.phase}' 2>/dev/null || echo "") + echo "Waiting... ($i/90) - Namespace status: $NS_STATUS" + + if [ "$i" -gt 30 ] && [ "$NS_STATUS" = "Terminating" ]; then + echo "Namespace stuck in Terminating, attempting force delete..." + kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o name 2>/dev/null | \ + xargs -n 1 kubectl patch -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -p '{"metadata":{"finalizers":[]}}' --type=merge 2>/dev/null || true + kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o json | \ + jq '.spec.finalizers = []' | \ + kubectl replace --raw "/api/v1/namespaces/${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}/finalize" -f - || true + sleep 5 + fi + + sleep 2 + done + + if kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null; then + echo "ERROR: Namespace still exists after timeout!" + kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml + exit 1 + fi + + echo "Creating namespace: ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" + kubectl create namespace ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} + + # AWS EKS values: TLS off, securityContext, tests, minimal components for integration tests + cat << 'VALUESEOF' | sed 's/^ //' > /tmp/pgskipper-aws-values.yaml + global: + tls: + enabled: false + securityContext: {} + operator: + image: ghcr.io/netcracker/pgskipper-operator:main + tls: + enabled: false + patroni: + clusterName: patroni + metricCollector: + install: true + dockerImage: ghcr.io/netcracker/pgskipper-docker-monitoring-agent:main + securityContext: {} + backupDaemon: + install: true + dockerImage: ghcr.io/netcracker/pgskipper-docker-backup-daemon:main + securityContext: {} + tests: + install: true + runTestScenarios: basic + affinity: null + VALUESEOF + + HELM_ARGS="" + HELM_ARGS="$HELM_ARGS --set tests.runTestScenarios=${{ github.event.inputs.test_scenario || 'basic' }}" + # Use tests image from this repo (build.yaml pushes to ghcr.io/owner/pgskipper-operator-tests:TAG) + GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') + IMAGE_TAG=$(echo ${{ github.ref }} | sed 's@refs/tags/@@;s@refs/heads/@@;s@/@_@g') + HELM_ARGS="$HELM_ARGS --set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" + + echo "Helm additional args: $HELM_ARGS" + + helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ + --create-namespace \ + -f ./operator/charts/patroni-services/values.yaml \ + -f /tmp/pgskipper-aws-values.yaml \ + pgskipper ./operator/charts/patroni-services \ + $HELM_ARGS + + echo "Waiting for operator and Patroni to be ready..." + kubectl wait --for=condition=ready pod -l name=postgres-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true + kubectl wait --for=condition=ready pod -l app=patroni -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=600s || true + + echo "Pgskipper installation completed" + kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} diff --git a/tests/Dockerfile b/tests/Dockerfile index 43606367..acf9f7a8 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/netcracker/qubership-docker-integration-tests:0.3.9 +FROM ghcr.io/elbe0116/qubership-docker-integration-tests:PSUPATPOS-63 ENV LC_ALL=en_US.UTF-8 \ LANG=en_US.UTF-8 @@ -7,7 +7,6 @@ COPY docker/pip.conf /root/.pip/pip.conf COPY docker/requirements.txt /root/requirements.txt RUN set -x \ - && apk add --update --no-cache build-base postgresql-dev \ && pip3 install --no-cache-dir -r /root/requirements.txt \ && pip3 uninstall -y pip From 451469766c6589bc52d4b692dd621adc32b44810 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 20 Feb 2026 14:06:33 +0500 Subject: [PATCH 02/37] add variable + regen generated files --- .../workflows/install_pgskipper_to_aws.yaml | 12 ++ operator/api/apps/v1/postgresservice_types.go | 1 + operator/api/apps/v1/zz_generated.deepcopy.go | 7 + operator/api/patroni/v1/patronicore_types.go | 1 + .../api/patroni/v1/zz_generated.deepcopy.go | 7 + .../crds/netcracker.com_patroniservices.yaml | 160 +++++++++++++++++- .../charts/patroni-services/templates/cr.yaml | 42 +++++ operator/charts/patroni-services/values.yaml | 13 ++ operator/pkg/deployment/tests.go | 6 + 9 files changed, 248 insertions(+), 1 deletion(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index b86c6a49..099c9444 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -127,6 +127,18 @@ jobs: GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') IMAGE_TAG=$(echo ${{ github.ref }} | sed 's@refs/tags/@@;s@refs/heads/@@;s@/@_@g') HELM_ARGS="$HELM_ARGS --set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" + # ATP/S3 storage for test results (secrets and vars) + [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" + [ -n "${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.password=${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" + [ -n "${{ vars.ATP_STORAGE_PROVIDER }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.provider=${{ vars.ATP_STORAGE_PROVIDER }}" + [ -n "${{ vars.ATP_STORAGE_SERVER_URL }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.serverUrl=${{ vars.ATP_STORAGE_SERVER_URL }}" + [ -n "${{ vars.ATP_STORAGE_SERVER_UI_URL }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.serverUiUrl=${{ vars.ATP_STORAGE_SERVER_UI_URL }}" + [ -n "${{ vars.ATP_STORAGE_BUCKET }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.bucket=${{ vars.ATP_STORAGE_BUCKET }}" + [ -n "${{ vars.AWS_REGION }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.region=${{ vars.AWS_REGION }}" + [ -n "${{ vars.ATP_REPORT_VIEW_UI_URL }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpReportViewUiUrl=${{ vars.ATP_REPORT_VIEW_UI_URL }}" + ENV_NAME="${{ vars.ENVIRONMENT_NAME }}" + [ -z "$ENV_NAME" ] && ENV_NAME="${{ github.event.inputs.environment }}" + [ -n "$ENV_NAME" ] && HELM_ARGS="$HELM_ARGS --set tests.environmentName=$ENV_NAME" echo "Helm additional args: $HELM_ARGS" diff --git a/operator/api/apps/v1/postgresservice_types.go b/operator/api/apps/v1/postgresservice_types.go index e6695222..0ff26e22 100644 --- a/operator/api/apps/v1/postgresservice_types.go +++ b/operator/api/apps/v1/postgresservice_types.go @@ -209,6 +209,7 @@ type IntegrationTests struct { PgNodeQty int `json:"pgNodeQty,omitempty"` PodLabels map[string]string `json:"podLabels,omitempty"` Affinity v1.Affinity `json:"affinity,omitempty"` + Env []v1.EnvVar `json:"env,omitempty"` } // ExternalDataBase defines the desired state of ExternalDataBase diff --git a/operator/api/apps/v1/zz_generated.deepcopy.go b/operator/api/apps/v1/zz_generated.deepcopy.go index 68b58c34..43963b5d 100644 --- a/operator/api/apps/v1/zz_generated.deepcopy.go +++ b/operator/api/apps/v1/zz_generated.deepcopy.go @@ -146,6 +146,13 @@ func (in *IntegrationTests) DeepCopyInto(out *IntegrationTests) { } } in.Affinity.DeepCopyInto(&out.Affinity) + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationTests. diff --git a/operator/api/patroni/v1/patronicore_types.go b/operator/api/patroni/v1/patronicore_types.go index 1c672f6a..cba5e314 100644 --- a/operator/api/patroni/v1/patronicore_types.go +++ b/operator/api/patroni/v1/patronicore_types.go @@ -188,6 +188,7 @@ type IntegrationTests struct { PgNodeQty int `json:"pgNodeQty,omitempty"` PodLabels map[string]string `json:"podLabels,omitempty"` Affinity v1.Affinity `json:"affinity,omitempty"` + Env []v1.EnvVar `json:"env,omitempty"` } type Policies struct { diff --git a/operator/api/patroni/v1/zz_generated.deepcopy.go b/operator/api/patroni/v1/zz_generated.deepcopy.go index a9ed7bd0..419eaefd 100644 --- a/operator/api/patroni/v1/zz_generated.deepcopy.go +++ b/operator/api/patroni/v1/zz_generated.deepcopy.go @@ -110,6 +110,13 @@ func (in *IntegrationTests) DeepCopyInto(out *IntegrationTests) { } } in.Affinity.DeepCopyInto(&out.Affinity) + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]corev1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationTests. diff --git a/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml b/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml index 436e2cc3..8400afe2 100644 --- a/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml +++ b/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml @@ -3553,6 +3553,163 @@ spec: x-kubernetes-list-type: atomic type: object type: object + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: |- + Name of the environment variable. + May consist of any printable ASCII characters except '='. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + fileKeyRef: + description: |- + FileKeyRef selects a key of the env file. + Requires the EnvFiles feature gate to be enabled. + properties: + key: + description: |- + The key within the env file. An invalid key will prevent the pod from starting. + The keys defined within a source may consist of any printable ASCII characters except '='. + During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. + type: string + optional: + default: false + description: |- + Specify whether the file or its key must be defined. If the file or key + does not exist, then the env var is not published. + If optional is set to true and the specified key does not exist, + the environment variable will not be set in the Pod's containers. + + If optional is set to false and the specified key does not exist, + an error will be returned during Pod creation. + type: boolean + path: + description: |- + The path within the volume from which to select the file. + Must be relative and may not contain the '..' path or start with '..'. + type: string + volumeName: + description: The name of the volume mount containing + the env file. + type: string + required: + - key + - path + - volumeName + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array image: type: string pgNodeQty: @@ -6139,9 +6296,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- diff --git a/operator/charts/patroni-services/templates/cr.yaml b/operator/charts/patroni-services/templates/cr.yaml index 33f5051b..8060d4d6 100644 --- a/operator/charts/patroni-services/templates/cr.yaml +++ b/operator/charts/patroni-services/templates/cr.yaml @@ -423,6 +423,48 @@ spec: {{- end }} {{ end }} pgNodeQty: {{ default "1" .Values.patroni.replicas }} + {{- if or .Values.tests.environmentName (and .Values.tests.atpStorage .Values.tests.atpStorage.provider) (and .Values.tests.env (len .Values.tests.env | gt 0)) }} + env: + {{- if .Values.tests.environmentName }} + - name: ENVIRONMENT_NAME + value: {{ .Values.tests.environmentName | quote }} + {{- end }} + {{- if .Values.tests.atpStorage.provider }} + - name: ATP_STORAGE_PROVIDER + value: {{ .Values.tests.atpStorage.provider | quote }} + {{- end }} + {{- if .Values.tests.atpStorage.serverUrl }} + - name: ATP_STORAGE_SERVER_URL + value: {{ .Values.tests.atpStorage.serverUrl | quote }} + {{- end }} + {{- if .Values.tests.atpStorage.serverUiUrl }} + - name: ATP_STORAGE_SERVER_UI_URL + value: {{ .Values.tests.atpStorage.serverUiUrl | quote }} + {{- end }} + {{- if .Values.tests.atpStorage.bucket }} + - name: ATP_STORAGE_BUCKET + value: {{ .Values.tests.atpStorage.bucket | quote }} + {{- end }} + {{- if .Values.tests.atpStorage.region }} + - name: ATP_STORAGE_REGION + value: {{ .Values.tests.atpStorage.region | quote }} + {{- end }} + {{- if .Values.tests.atpStorage.username }} + - name: ATP_STORAGE_USERNAME + value: {{ .Values.tests.atpStorage.username | quote }} + {{- end }} + {{- if .Values.tests.atpStorage.password }} + - name: ATP_STORAGE_PASSWORD + value: {{ .Values.tests.atpStorage.password | quote }} + {{- end }} + {{- if .Values.tests.atpReportViewUiUrl }} + - name: ATP_REPORT_VIEW_UI_URL + value: {{ .Values.tests.atpReportViewUiUrl | quote }} + {{- end }} + {{- if .Values.tests.env }} + {{- toYaml .Values.tests.env | nindent 4 }} + {{- end }} + {{- end }} {{ end }} {{ if .Values.runTestsOnly }} runTestsTime: {{ now | unixEpoch | quote }} diff --git a/operator/charts/patroni-services/values.yaml b/operator/charts/patroni-services/values.yaml index 0374f46b..22726b2a 100644 --- a/operator/charts/patroni-services/values.yaml +++ b/operator/charts/patroni-services/values.yaml @@ -431,6 +431,19 @@ tests: install: true dockerImage: ghcr.io/netcracker/pgskipper-operator-tests:main podLabels: {} + # S3/ATP storage for test results upload (e.g. AWS install, run_tests pipeline) + atpStorage: + provider: "aws" + serverUrl: "" + serverUiUrl: "" + bucket: "" + region: "us-east-1" + username: "" + password: "" + atpReportViewUiUrl: "" + environmentName: "" + # Extra env vars for integration test pod (optional) + env: [] # One of "full", "basic"or one from testScenarios runTestScenarios: "basic" testScenarios: diff --git a/operator/pkg/deployment/tests.go b/operator/pkg/deployment/tests.go index ecd2e663..53153efa 100644 --- a/operator/pkg/deployment/tests.go +++ b/operator/pkg/deployment/tests.go @@ -134,6 +134,9 @@ func NewIntegrationTestsPod(cr *v1.PatroniServices, cluster *patroniv1.PatroniCl RestartPolicy: corev1.RestartPolicyNever, }, } + if len(testsSpec.Env) > 0 { + pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, testsSpec.Env...) + } if testsSpec.Resources != nil { pod.Spec.Containers[0].Resources = *testsSpec.Resources } @@ -255,6 +258,9 @@ func NewCoreIntegrationTests(cr *patroniv1.PatroniCore, cluster *patroniv1.Patro RestartPolicy: corev1.RestartPolicyNever, }, } + if len(testsSpec.Env) > 0 { + pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, testsSpec.Env...) + } if testsSpec.Resources != nil { pod.Spec.Containers[0].Resources = *testsSpec.Resources } From a7495777e5e225e61d59bc5a537f392e2fa66787 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 20 Feb 2026 14:13:46 +0500 Subject: [PATCH 03/37] fix build --- .github/workflows/build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fe0aecc7..b43d61b9 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -154,6 +154,7 @@ jobs: tags: ghcr.io/${{ env.GITHUB_GROUP }}/${{ matrix.component.name }}:${{ env.TAG_NAME }} provenance: false - uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0 + continue-on-error: true with: package-name: ${{ matrix.component.name }} package-type: 'container' From 96f5cfc990c994ad17e537232570cbdf875dd9a6 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 20 Feb 2026 14:16:27 +0500 Subject: [PATCH 04/37] fix --- .github/workflows/build.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index b43d61b9..6a9508f0 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -81,13 +81,17 @@ jobs: components=$(jq -c '.components' .github/build-config.cfg) fi if [[ "$GITHUB_EVENT_NAME" == "pull_request" || "$GITHUB_EVENT_NAME" == "push" ]]; then - all_changed_files=$(jq -c '.' .github/outputs/all_changed_files.json) + all_changed_files=$(jq -c '.' .github/outputs/all_changed_files.json 2>/dev/null || echo "null") if [ "$all_changed_files" != "null" ]; then echo "all_changed_files=${all_changed_files}" chmod +x .github/scripts/matrix.sh components=$(./.github/scripts/matrix.sh ".github/build-config.cfg" ".github/outputs/all_changed_files.json") fi fi + if [ -z "$components" ] || [ "$components" = "[]" ]; then + echo "No components from matrix, using all from build-config.cfg" + components=$(jq -c '.components' .github/build-config.cfg) + fi echo "components=${components}" echo "components=${components}" >> "$GITHUB_OUTPUT" echo "platforms=$(jq -c '.platforms' .github/build-config.cfg)" >> "$GITHUB_OUTPUT" From 44b7005c5060611838062f8fc1486015a33ca639 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 20 Feb 2026 14:23:28 +0500 Subject: [PATCH 05/37] packages --- tests/docker/requirements.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/docker/requirements.txt b/tests/docker/requirements.txt index 6effed82..b7a434b9 100644 --- a/tests/docker/requirements.txt +++ b/tests/docker/requirements.txt @@ -1,3 +1,4 @@ +# Keep same set as before; only fix versions that break build/K8s SSL (align with base image) aniso8601==10.0.1 cachetools==4.2.4 certifi==2024.7.4 @@ -11,10 +12,12 @@ importlib-metadata==4.11.3 ipaddress==1.0.23 itsdangerous==2.1.2 Jinja2==3.1.6 -kubernetes==31.0.0 +# Align with base image to avoid K8s API/SSL errors (was 31.0.0) +kubernetes==34.1.0 MarkupSafe==3.0.3 oauthlib==3.3.1 -psycopg2==2.9.10 +# Binary build — no build-base/postgresql-dev needed (was psycopg2==2.9.10) +psycopg2-binary==2.9.10 pyasn1==0.6.2 pyasn1-modules==0.4.2 PyJWT==2.4.0 @@ -31,7 +34,8 @@ ruamel.yaml==0.19.1 ruamel.yaml.clib==0.2.12 six==1.16.0 typing_extensions==4.2.0 -urllib3==2.6.3 +# Align with base image (was 2.6.3) +urllib3~=2.3.0 websocket-client==1.9.0 Werkzeug==3.1.5 zipp==3.23.0 From c311ffb66c2e04cce54c579b120c28b4fa0eb3b8 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 20 Feb 2026 14:32:50 +0500 Subject: [PATCH 06/37] fix versions --- tests/docker/requirements.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/docker/requirements.txt b/tests/docker/requirements.txt index b7a434b9..683d2956 100644 --- a/tests/docker/requirements.txt +++ b/tests/docker/requirements.txt @@ -1,4 +1,5 @@ -# Keep same set as before; only fix versions that break build/K8s SSL (align with base image) +# Keep same set as before; align versions with base image to avoid pip conflicts +allure-robotframework==2.15.0 aniso8601==10.0.1 cachetools==4.2.4 certifi==2024.7.4 @@ -20,15 +21,18 @@ oauthlib==3.3.1 psycopg2-binary==2.9.10 pyasn1==0.6.2 pyasn1-modules==0.4.2 -PyJWT==2.4.0 +# Match base image (was 2.4.0) +pyjwt==2.10.1 python-dateutil==2.8.2 python-string-utils==1.0.0 pytz==2022.1 requests==2.32.5 requests-oauthlib==2.0.0 -robotframework==5.0 +# Match base image — avoid downgrade conflict (was 5.0) +robotframework==7.3.2 robotframework-databaselibrary==1.2.4 -robotframework-requests==0.9 +# Match base image (was 0.9) +robotframework-requests==0.9.7 rsa==4.8 ruamel.yaml==0.19.1 ruamel.yaml.clib==0.2.12 From f6bf3fa0ef7b7f30738376033cf757dead3667c0 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 20 Feb 2026 14:37:52 +0500 Subject: [PATCH 07/37] add access rights --- tests/Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/Dockerfile b/tests/Dockerfile index acf9f7a8..01d31589 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -3,6 +3,9 @@ FROM ghcr.io/elbe0116/qubership-docker-integration-tests:PSUPATPOS-63 ENV LC_ALL=en_US.UTF-8 \ LANG=en_US.UTF-8 +# Base image may run as non-root; install as root so /root and site-packages are writable +USER root + COPY docker/pip.conf /root/.pip/pip.conf COPY docker/requirements.txt /root/requirements.txt From f67785cf83b88858c6e9956642ac371637ea468e Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Mon, 23 Feb 2026 16:27:59 +0500 Subject: [PATCH 08/37] fix config --- .../workflows/install_pgskipper_to_aws.yaml | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 099c9444..a34ae524 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -21,6 +21,22 @@ jobs: - name: Checkout Code uses: actions/checkout@v4 + - name: Validate required Environment variables + run: | + MISSING="" + [ -z "${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" ] && MISSING="${MISSING} PGSKIPPER_INSTALL_NAMESPACE" + [ -z "${{ vars.AWS_CLUSTERNAME }}" ] && MISSING="${MISSING} AWS_CLUSTERNAME" + [ -z "${{ secrets.AWS_ACCESS_KEY_ID }}" ] && MISSING="${MISSING} AWS_ACCESS_KEY_ID" + [ -z "${{ secrets.AWS_SECRET_ACCESS_KEY }}" ] && MISSING="${MISSING} AWS_SECRET_ACCESS_KEY" + if [ -n "$MISSING" ]; then + echo "::error::Missing required configuration in Environment '${{ github.event.inputs.environment }}'. Set the following Variables (and Secrets):$MISSING" + echo "In GitHub: Settings → Environments → ${{ github.event.inputs.environment }} → add Variables: PGSKIPPER_INSTALL_NAMESPACE, AWS_CLUSTERNAME; Secrets: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY. Optionally AWS_REGION (default us-east-1)." + exit 1 + fi + echo "PGSKIPPER_INSTALL_NAMESPACE=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" + echo "AWS_CLUSTERNAME=${{ vars.AWS_CLUSTERNAME }}" + echo "AWS_REGION=${{ vars.AWS_REGION || 'us-east-1' }}" + - name: Listing downloaded files run: | pwd @@ -95,6 +111,17 @@ jobs: echo "Creating namespace: ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" kubectl create namespace ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} + # Operator requires these secrets; chart only creates them when externalDataBase is set + echo "Creating postgres-credentials and replicator-credentials secrets..." + kubectl create secret generic postgres-credentials -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ + --from-literal=username=postgres \ + --from-literal=password='p@ssWOrD1' \ + --dry-run=client -o yaml | kubectl apply -f - + kubectl create secret generic replicator-credentials -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ + --from-literal=username=replicator \ + --from-literal=password=replicator \ + --dry-run=client -o yaml | kubectl apply -f - + # AWS EKS values: TLS off, securityContext, tests, minimal components for integration tests cat << 'VALUESEOF' | sed 's/^ //' > /tmp/pgskipper-aws-values.yaml global: From 7bc6227521f76b36c87e058cac83af1d757d8c36 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Mon, 23 Feb 2026 17:29:54 +0500 Subject: [PATCH 09/37] volume --- .github/workflows/install_pgskipper_to_aws.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index a34ae524..ad0b25ef 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -142,6 +142,8 @@ jobs: install: true dockerImage: ghcr.io/netcracker/pgskipper-docker-backup-daemon:main securityContext: {} + storage: + storageClass: gp2 tests: install: true runTestScenarios: basic From fa6e96e6fa21e69d84e9790921ad27ba97dbfa3a Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Mon, 23 Feb 2026 18:32:45 +0500 Subject: [PATCH 10/37] security context --- .github/workflows/install_pgskipper_to_aws.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index ad0b25ef..5edf4b55 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -123,11 +123,13 @@ jobs: --dry-run=client -o yaml | kubectl apply -f - # AWS EKS values: TLS off, securityContext, tests, minimal components for integration tests + # GLOBAL_SECURITY_CONTEXT: false so operator does not set runAsNonRoot on containers (backup-daemon image runs as root) cat << 'VALUESEOF' | sed 's/^ //' > /tmp/pgskipper-aws-values.yaml global: tls: enabled: false securityContext: {} + GLOBAL_SECURITY_CONTEXT: "false" operator: image: ghcr.io/netcracker/pgskipper-operator:main tls: From 047e60c6536aff7e10c31205fc442ed3dc2583fe Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Tue, 24 Feb 2026 14:17:20 +0500 Subject: [PATCH 11/37] fix daemon configuration --- .github/workflows/install_pgskipper_to_aws.yaml | 3 ++- operator/pkg/reconciler/backup_daemon.go | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 5edf4b55..95535b68 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -143,7 +143,8 @@ jobs: backupDaemon: install: true dockerImage: ghcr.io/netcracker/pgskipper-docker-backup-daemon:main - securityContext: {} + securityContext: + runAsNonRoot: false storage: storageClass: gp2 tests: diff --git a/operator/pkg/reconciler/backup_daemon.go b/operator/pkg/reconciler/backup_daemon.go index d15cf9c7..4d9c30de 100644 --- a/operator/pkg/reconciler/backup_daemon.go +++ b/operator/pkg/reconciler/backup_daemon.go @@ -97,8 +97,10 @@ func (r *BackupDaemonReconciler) Reconcile() error { return err } - //Adding securityContexts - backupDaemonDeployment.Spec.Template.Spec.Containers[0].SecurityContext = util.GetDefaultSecurityContext() + // Adding securityContexts: when CR explicitly sets runAsNonRoot=false (e.g. for root-based backup-daemon image), do not set container SecurityContext; otherwise use default + if bdSpec.SecurityContext.RunAsNonRoot == nil || *bdSpec.SecurityContext.RunAsNonRoot { + backupDaemonDeployment.Spec.Template.Spec.Containers[0].SecurityContext = util.GetDefaultSecurityContext() + } // External database Section if cr.Spec.ExternalDataBase != nil { From 2c01d91d5a2737c6aee960a377b29a8de724b3ad Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Tue, 24 Feb 2026 17:29:34 +0500 Subject: [PATCH 12/37] install pg patroni --- .../workflows/install_pgskipper_to_aws.yaml | 19 ++++++++++++++++--- .../patroni-services/templates/_helpers.tpl | 9 +++++---- operator/charts/patroni-services/values.yaml | 1 + 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 95535b68..bb161e6f 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -72,8 +72,9 @@ jobs: - name: Install Pgskipper (Patroni Services) run: | - # Uninstall Helm release first (cleans CRs and avoids finalizers blocking namespace deletion) - echo "Uninstalling Helm release..." + # Uninstall Helm releases first (cleans CRs and avoids finalizers blocking namespace deletion) + echo "Uninstalling Helm releases..." + helm uninstall pgskipper-patroni -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --wait --timeout=120s || true helm uninstall pgskipper -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --wait --timeout=120s || true echo "Deleting namespace..." @@ -181,8 +182,20 @@ jobs: pgskipper ./operator/charts/patroni-services \ $HELM_ARGS - echo "Waiting for operator and Patroni to be ready..." + # Install patroni-core so Patroni pods and Service pg-patroni are created (by operator with OPERATOR_ROLE=patroni). + # Without this, only PatroniServices controller runs and backup daemon has no Postgres to connect to. + echo "Installing patroni-core chart (PatroniCore CR + patroni-core-operator)..." + helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ + --create-namespace \ + -f ./operator/charts/patroni-core/values.yaml \ + --set tls.enabled=false \ + --set patroni.clusterName=patroni \ + --set operator.image=ghcr.io/netcracker/pgskipper-operator:main \ + pgskipper-patroni ./operator/charts/patroni-core + + echo "Waiting for operators and Patroni to be ready..." kubectl wait --for=condition=ready pod -l name=postgres-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true + kubectl wait --for=condition=ready pod -l name=patroni-core-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true kubectl wait --for=condition=ready pod -l app=patroni -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=600s || true echo "Pgskipper installation completed" diff --git a/operator/charts/patroni-services/templates/_helpers.tpl b/operator/charts/patroni-services/templates/_helpers.tpl index 728cdb0a..e6c283ae 100644 --- a/operator/charts/patroni-services/templates/_helpers.tpl +++ b/operator/charts/patroni-services/templates/_helpers.tpl @@ -248,19 +248,20 @@ Get TLS secret name for services {{- end -}} {{/* -Postgres host for backup daemon +Postgres host for backup daemon. Uses FQDN (.svc.cluster.local) for reliable DNS in Kubernetes (e.g. EKS). */}} {{- define "backupDaemon.pgHost" -}} +{{- $clusterDomain := default "cluster.local" .Values.backupDaemon.clusterDomain -}} {{- if .Values.connectionPooler.install -}} pg-{{ default "patroni" .Values.patroni.clusterName }}-direct {{- else if .Values.externalDataBase }} {{- if or (eq (lower .Values.externalDataBase.type) "azure") (eq (lower .Values.externalDataBase.type) "rds") }} -{{- printf "%s.%s" "pg-patroni" .Release.Namespace }} +{{- printf "pg-patroni.%s.svc.%s" .Release.Namespace $clusterDomain }} {{- else }} -{{- default (printf "%s.%s" "pg-patroni" .Release.Namespace) .Values.backupDaemon.pgHost }} +{{- default (printf "pg-patroni.%s.svc.%s" .Release.Namespace $clusterDomain) .Values.backupDaemon.pgHost }} {{- end }} {{- else }} -{{- default (printf "%s.%s" "pg-patroni" .Release.Namespace) .Values.backupDaemon.pgHost }} +{{- default (printf "pg-patroni.%s.svc.%s" .Release.Namespace $clusterDomain) .Values.backupDaemon.pgHost }} {{- end }} {{- end -}} diff --git a/operator/charts/patroni-services/values.yaml b/operator/charts/patroni-services/values.yaml index 22726b2a..793589d4 100644 --- a/operator/charts/patroni-services/values.yaml +++ b/operator/charts/patroni-services/values.yaml @@ -177,6 +177,7 @@ backupDaemon: backupSchedule: "0 0/7 * * *" # pgHost: pg-patroni.postgres-service + # clusterDomain: cluster.local # K8s cluster DNS domain; used to build FQDN for pgHost (e.g. pg-patroni.NAMESPACE.svc.cluster.local) # The eviction policy for backup daemon: period and action. The default value is 7d/delete. evictionPolicy: "7d/delete" backupTimeout: 300 From 17e68cb40656161354a839622b73aff13c260414 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 12:53:22 +0500 Subject: [PATCH 13/37] Update Dockerfile -- trigger From 55ea8841017191a838a5e5167dc05e3d7bd93215 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 13:01:32 +0500 Subject: [PATCH 14/37] fix for patroni --- .../workflows/install_pgskipper_to_aws.yaml | 22 +++++++++++++++++++ .../templates/secrets/postgres-secret.yaml | 2 +- .../templates/secrets/replicator-secret.yaml | 2 ++ operator/charts/patroni-core/values.yaml | 3 +++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index bb161e6f..ce189c1e 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -191,8 +191,30 @@ jobs: --set tls.enabled=false \ --set patroni.clusterName=patroni \ --set operator.image=ghcr.io/netcracker/pgskipper-operator:main \ + --set patroni.storage.storageClass=gp2 \ + --set patroni.fsGroup=26 \ + --set GLOBAL_SECURITY_CONTEXT="false" \ + --set createCredentials=false \ pgskipper-patroni ./operator/charts/patroni-core + echo "State after patroni-core install (diagnostics)..." + kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o wide + kubectl get patronicores -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null || true + kubectl get statefulset,svc,pvc -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null || true + POD=$(kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -l name=patroni-core-operator -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true) + if [ -n "$POD" ]; then + echo "--- patroni-core-operator pod status ---" + kubectl get pod "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o wide + if [ "$(kubectl get pod "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.status.phase}' 2>/dev/null)" != "Running" ]; then + echo "--- describe (patroni-core-operator not Running) ---" + kubectl describe pod "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} | tail -80 + echo "--- logs ---" + kubectl logs "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --tail=100 2>/dev/null || true + fi + else + echo "No patroni-core-operator pod found." + fi + echo "Waiting for operators and Patroni to be ready..." kubectl wait --for=condition=ready pod -l name=postgres-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true kubectl wait --for=condition=ready pod -l name=patroni-core-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true diff --git a/operator/charts/patroni-core/templates/secrets/postgres-secret.yaml b/operator/charts/patroni-core/templates/secrets/postgres-secret.yaml index 029744f3..c3445da0 100644 --- a/operator/charts/patroni-core/templates/secrets/postgres-secret.yaml +++ b/operator/charts/patroni-core/templates/secrets/postgres-secret.yaml @@ -1,4 +1,4 @@ -{{- if .Values.patroni.install }} +{{- if and .Values.patroni.install (ne (index .Values "createCredentials") false) }} apiVersion: v1 kind: Secret metadata: diff --git a/operator/charts/patroni-core/templates/secrets/replicator-secret.yaml b/operator/charts/patroni-core/templates/secrets/replicator-secret.yaml index a9eb3e06..64600b95 100644 --- a/operator/charts/patroni-core/templates/secrets/replicator-secret.yaml +++ b/operator/charts/patroni-core/templates/secrets/replicator-secret.yaml @@ -1,3 +1,4 @@ +{{- if ne (index .Values "createCredentials") false }} apiVersion: v1 kind: Secret metadata: @@ -10,3 +11,4 @@ data: password: {{ .Values.replicatorPassword | b64enc }} username: {{ "replicator" | b64enc }} type: Opaque +{{- end }} diff --git a/operator/charts/patroni-core/values.yaml b/operator/charts/patroni-core/values.yaml index 1c15367d..0fdaeab2 100644 --- a/operator/charts/patroni-core/values.yaml +++ b/operator/charts/patroni-core/values.yaml @@ -7,6 +7,9 @@ postgresUser: postgres # The password for the database superuser. postgresPassword: "p@ssWOrD1" replicatorPassword: "replicator" +# Set to false when postgres-credentials and replicator-credentials are created outside the chart (e.g. by CI). +# When false, the chart skips creating these secrets so Helm install does not conflict with existing resources. +# createCredentials: true podLabels: {} From 1cdbc458b08c0c6e278c11c5b53b019f48aa45eb Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 14:04:01 +0500 Subject: [PATCH 15/37] fix install --- .github/workflows/install_pgskipper_to_aws.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index ce189c1e..d2a2a451 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -184,6 +184,7 @@ jobs: # Install patroni-core so Patroni pods and Service pg-patroni are created (by operator with OPERATOR_ROLE=patroni). # Without this, only PatroniServices controller runs and backup daemon has no Postgres to connect to. + # patroni.fsGroup=26 makes the data volume writable by postgres user (GID 26), required to avoid "Permission denied" on /var/lib/pgsql/data. echo "Installing patroni-core chart (PatroniCore CR + patroni-core-operator)..." helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ --create-namespace \ From f7a1471cd52422d6dba010aac2a968b31f7cd20b Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 14:09:23 +0500 Subject: [PATCH 16/37] fix install --- .github/workflows/install_pgskipper_to_aws.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index d2a2a451..a1d2032f 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -184,7 +184,7 @@ jobs: # Install patroni-core so Patroni pods and Service pg-patroni are created (by operator with OPERATOR_ROLE=patroni). # Without this, only PatroniServices controller runs and backup daemon has no Postgres to connect to. - # patroni.fsGroup=26 makes the data volume writable by postgres user (GID 26), required to avoid "Permission denied" on /var/lib/pgsql/data. + # patroni.fsGroup=26 so data volume is writable by postgres user (GID 26), avoids "Permission denied" on /var/lib/pgsql/data. echo "Installing patroni-core chart (PatroniCore CR + patroni-core-operator)..." helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ --create-namespace \ From 0809595d1e8df4d997e5e2d16203add714884678 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 15:29:05 +0500 Subject: [PATCH 17/37] fix for aws --- operator/charts/patroni-core/templates/cr.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/operator/charts/patroni-core/templates/cr.yaml b/operator/charts/patroni-core/templates/cr.yaml index 4643ab0c..dbedf375 100644 --- a/operator/charts/patroni-core/templates/cr.yaml +++ b/operator/charts/patroni-core/templates/cr.yaml @@ -40,9 +40,15 @@ spec: synchronousMode: {{ default "false" .Values.patroni.synchronousMode }} createEndpoint: {{ default "false" .Values.patroni.createEndpoint }} {{ end }} - {{ if .Values.patroni.fsGroup }} - fsGroup: {{ .Values.patroni.fsGroup }} - {{ end }} + {{- if or .Values.patroni.securityContext .Values.patroni.fsGroup }} + securityContext: + {{- if .Values.patroni.securityContext }} + {{- toYaml .Values.patroni.securityContext | nindent 6 }} + {{- end }} + {{- if .Values.patroni.fsGroup }} + fsGroup: {{ .Values.patroni.fsGroup }} + {{- end }} + {{- end }} {{ if .Values.patroni.affinity }} affinity: {{ .Values.patroni.affinity | toJson }} {{ else if .Values.affinity }} From 090f69b8dc36af5c7edb4f2e9ff943788209b5ee Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 16:13:46 +0500 Subject: [PATCH 18/37] fix context --- operator/charts/patroni-core/values.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/operator/charts/patroni-core/values.yaml b/operator/charts/patroni-core/values.yaml index 0fdaeab2..5b095814 100644 --- a/operator/charts/patroni-core/values.yaml +++ b/operator/charts/patroni-core/values.yaml @@ -104,9 +104,8 @@ patroni: podLabels: {} podAnnotations: {} configMapAnnotations: {} - # patroni.fsGroup. Specifies fsGroup that will be used in Patroni Pod Spec. - # Useful in case of k8s installation and provisioned storage - # fsGroup: 26 + # patroni.fsGroup. Specifies fsGroup that will be used in Patroni Pod Spec (data volume writable by postgres GID 26). + fsGroup: 26 # affinity: { # "podAffinity": { # "preferredDuringSchedulingIgnoredDuringExecution": [ @@ -137,7 +136,6 @@ patroni: # ] # } # } - # fsGroup: 26 # patroni.affinity parameter. Configures a `podAffinityTerm`. Possible values are required and preferred # synchronousMode: true From a31e2e1719dfb7facd38d46348acc574172fe275 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 16:30:40 +0500 Subject: [PATCH 19/37] versions --- .../workflows/install_pgskipper_to_aws.yaml | 34 ++++++++++++++----- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index a1d2032f..57238975 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -11,6 +11,14 @@ on: description: "Test scenario (e.g. basic, patroniBasic, or empty for default)" required: false default: "basic" + repository: + description: "Repo to checkout (e.g. owner/pgskipper-operator-demo). Empty = this repo (use when running from your fork)." + required: false + default: "" + ref: + description: "Branch or ref to checkout (e.g. main). Used only if repository is set." + required: false + default: "main" jobs: Install-Pgskipper: @@ -20,6 +28,9 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 + with: + repository: ${{ github.event.inputs.repository || github.repository }} + ref: ${{ github.event.inputs.ref || github.ref }} - name: Validate required Environment variables run: | @@ -123,6 +134,11 @@ jobs: --from-literal=password=replicator \ --dry-run=client -o yaml | kubectl apply -f - + # Image registry/tag from the repo that triggered the workflow (your fork = your images) + GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') + IMAGE_TAG=$(echo ${{ github.ref }} | sed 's@refs/tags/@@;s@refs/heads/@@;s@/@_@g') + echo "Using images: ghcr.io/${GITHUB_GROUP}/...:${IMAGE_TAG}" + # AWS EKS values: TLS off, securityContext, tests, minimal components for integration tests # GLOBAL_SECURITY_CONTEXT: false so operator does not set runAsNonRoot on containers (backup-daemon image runs as root) cat << 'VALUESEOF' | sed 's/^ //' > /tmp/pgskipper-aws-values.yaml @@ -131,19 +147,15 @@ jobs: enabled: false securityContext: {} GLOBAL_SECURITY_CONTEXT: "false" - operator: - image: ghcr.io/netcracker/pgskipper-operator:main tls: enabled: false patroni: clusterName: patroni metricCollector: install: true - dockerImage: ghcr.io/netcracker/pgskipper-docker-monitoring-agent:main securityContext: {} backupDaemon: install: true - dockerImage: ghcr.io/netcracker/pgskipper-docker-backup-daemon:main securityContext: runAsNonRoot: false storage: @@ -155,10 +167,10 @@ jobs: VALUESEOF HELM_ARGS="" + HELM_ARGS="$HELM_ARGS --set operator.image=ghcr.io/${GITHUB_GROUP}/pgskipper-operator:${IMAGE_TAG}" + HELM_ARGS="$HELM_ARGS --set metricCollector.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-monitoring-agent:${IMAGE_TAG}" + HELM_ARGS="$HELM_ARGS --set backupDaemon.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-backup-daemon:${IMAGE_TAG}" HELM_ARGS="$HELM_ARGS --set tests.runTestScenarios=${{ github.event.inputs.test_scenario || 'basic' }}" - # Use tests image from this repo (build.yaml pushes to ghcr.io/owner/pgskipper-operator-tests:TAG) - GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') - IMAGE_TAG=$(echo ${{ github.ref }} | sed 's@refs/tags/@@;s@refs/heads/@@;s@/@_@g') HELM_ARGS="$HELM_ARGS --set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" # ATP/S3 storage for test results (secrets and vars) [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" @@ -191,7 +203,8 @@ jobs: -f ./operator/charts/patroni-core/values.yaml \ --set tls.enabled=false \ --set patroni.clusterName=patroni \ - --set operator.image=ghcr.io/netcracker/pgskipper-operator:main \ + --set operator.image=ghcr.io/${GITHUB_GROUP}/pgskipper-operator:${IMAGE_TAG} \ + --set patroni.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-patroni-16:${IMAGE_TAG} \ --set patroni.storage.storageClass=gp2 \ --set patroni.fsGroup=26 \ --set GLOBAL_SECURITY_CONTEXT="false" \ @@ -199,6 +212,11 @@ jobs: pgskipper-patroni ./operator/charts/patroni-core echo "State after patroni-core install (diagnostics)..." + echo "--- Checkout ref (chart source): ${{ github.ref }} ${{ github.sha }} ---" + echo "--- PatroniCore spec.patroni (securityContext must have fsGroup: 26) ---" + kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.items[0].spec.patroni}' 2>/dev/null | python3 -m json.tool 2>/dev/null || kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | head -80 + echo "--- StatefulSet pod template securityContext ---" + kubectl get statefulset -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -l pgcluster=patroni -o jsonpath='{.items[0].spec.template.spec.securityContext}' 2>/dev/null | python3 -m json.tool 2>/dev/null || echo "(none or not found)" kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o wide kubectl get patronicores -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null || true kubectl get statefulset,svc,pvc -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null || true From 24ea23a2ccc031c0b3c1cdf60fc906a0797f6c07 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 17:28:49 +0500 Subject: [PATCH 20/37] add logs to investigate --- .github/workflows/install_pgskipper_to_aws.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 57238975..2602b14e 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -197,6 +197,8 @@ jobs: # Install patroni-core so Patroni pods and Service pg-patroni are created (by operator with OPERATOR_ROLE=patroni). # Without this, only PatroniServices controller runs and backup daemon has no Postgres to connect to. # patroni.fsGroup=26 so data volume is writable by postgres user (GID 26), avoids "Permission denied" on /var/lib/pgsql/data. + echo "--- Chart CR manifest (patroni.securityContext must appear) ---" + helm template pgskipper-patroni ./operator/charts/patroni-core -f ./operator/charts/patroni-core/values.yaml --set patroni.fsGroup=26 --set createCredentials=false -s templates/cr.yaml 2>/dev/null | grep -A3 securityContext || echo "(no securityContext in rendered CR - check cr.yaml and values)" echo "Installing patroni-core chart (PatroniCore CR + patroni-core-operator)..." helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ --create-namespace \ @@ -239,5 +241,7 @@ jobs: kubectl wait --for=condition=ready pod -l name=patroni-core-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true kubectl wait --for=condition=ready pod -l app=patroni -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=600s || true + echo "--- StatefulSet securityContext (after operator reconcile; must have fsGroup: 26) ---" + kubectl get statefulset -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -l pgcluster=patroni -o jsonpath='{.items[0].spec.template.spec.securityContext}' 2>/dev/null | python3 -m json.tool 2>/dev/null || echo "(none or not found)" echo "Pgskipper installation completed" kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} From 59a24a11d4e94d523197743665c026fed698cb96 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 17:40:47 +0500 Subject: [PATCH 21/37] context --- .github/workflows/install_pgskipper_to_aws.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 2602b14e..80e21e03 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -209,6 +209,7 @@ jobs: --set patroni.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-patroni-16:${IMAGE_TAG} \ --set patroni.storage.storageClass=gp2 \ --set patroni.fsGroup=26 \ + --set patroni.securityContext.fsGroup=26 \ --set GLOBAL_SECURITY_CONTEXT="false" \ --set createCredentials=false \ pgskipper-patroni ./operator/charts/patroni-core From 5793d033abc943887f346efdd9ea6a906bf7138f Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 17:50:46 +0500 Subject: [PATCH 22/37] change image --- .github/workflows/install_pgskipper_to_aws.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 80e21e03..5b8f2e94 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -212,6 +212,7 @@ jobs: --set patroni.securityContext.fsGroup=26 \ --set GLOBAL_SECURITY_CONTEXT="false" \ --set createCredentials=false \ + --set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG} \ pgskipper-patroni ./operator/charts/patroni-core echo "State after patroni-core install (diagnostics)..." From 323fbb0ee32b1839472e72e150fb932183475d15 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 18:03:44 +0500 Subject: [PATCH 23/37] variables propagation --- .../workflows/install_pgskipper_to_aws.yaml | 30 ++++++------- .../charts/patroni-core/templates/cr.yaml | 42 +++++++++++++++++++ 2 files changed, 58 insertions(+), 14 deletions(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 5b8f2e94..c6f007a1 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -166,24 +166,26 @@ jobs: affinity: null VALUESEOF + ENV_NAME="${{ vars.ENVIRONMENT_NAME }}" + [ -z "$ENV_NAME" ] && ENV_NAME="${{ github.event.inputs.environment }}" + # Tests image + ATP/S3 storage for test results (used by both patroni-services and patroni-core) + TESTS_ATP_ARGS="--set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" + [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" + [ -n "${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.password=${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" + [ -n "${{ vars.ATP_STORAGE_PROVIDER }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.provider=${{ vars.ATP_STORAGE_PROVIDER }}" + [ -n "${{ vars.ATP_STORAGE_SERVER_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUrl=${{ vars.ATP_STORAGE_SERVER_URL }}" + [ -n "${{ vars.ATP_STORAGE_SERVER_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUiUrl=${{ vars.ATP_STORAGE_SERVER_UI_URL }}" + [ -n "${{ vars.ATP_STORAGE_BUCKET }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.bucket=${{ vars.ATP_STORAGE_BUCKET }}" + [ -n "${{ vars.AWS_REGION }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.region=${{ vars.AWS_REGION }}" + [ -n "${{ vars.ATP_REPORT_VIEW_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpReportViewUiUrl=${{ vars.ATP_REPORT_VIEW_UI_URL }}" + [ -n "$ENV_NAME" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.environmentName=$ENV_NAME" + HELM_ARGS="" HELM_ARGS="$HELM_ARGS --set operator.image=ghcr.io/${GITHUB_GROUP}/pgskipper-operator:${IMAGE_TAG}" HELM_ARGS="$HELM_ARGS --set metricCollector.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-monitoring-agent:${IMAGE_TAG}" HELM_ARGS="$HELM_ARGS --set backupDaemon.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-backup-daemon:${IMAGE_TAG}" HELM_ARGS="$HELM_ARGS --set tests.runTestScenarios=${{ github.event.inputs.test_scenario || 'basic' }}" - HELM_ARGS="$HELM_ARGS --set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" - # ATP/S3 storage for test results (secrets and vars) - [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" - [ -n "${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.password=${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" - [ -n "${{ vars.ATP_STORAGE_PROVIDER }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.provider=${{ vars.ATP_STORAGE_PROVIDER }}" - [ -n "${{ vars.ATP_STORAGE_SERVER_URL }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.serverUrl=${{ vars.ATP_STORAGE_SERVER_URL }}" - [ -n "${{ vars.ATP_STORAGE_SERVER_UI_URL }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.serverUiUrl=${{ vars.ATP_STORAGE_SERVER_UI_URL }}" - [ -n "${{ vars.ATP_STORAGE_BUCKET }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.bucket=${{ vars.ATP_STORAGE_BUCKET }}" - [ -n "${{ vars.AWS_REGION }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpStorage.region=${{ vars.AWS_REGION }}" - [ -n "${{ vars.ATP_REPORT_VIEW_UI_URL }}" ] && HELM_ARGS="$HELM_ARGS --set tests.atpReportViewUiUrl=${{ vars.ATP_REPORT_VIEW_UI_URL }}" - ENV_NAME="${{ vars.ENVIRONMENT_NAME }}" - [ -z "$ENV_NAME" ] && ENV_NAME="${{ github.event.inputs.environment }}" - [ -n "$ENV_NAME" ] && HELM_ARGS="$HELM_ARGS --set tests.environmentName=$ENV_NAME" + HELM_ARGS="$HELM_ARGS $TESTS_ATP_ARGS" echo "Helm additional args: $HELM_ARGS" @@ -212,7 +214,7 @@ jobs: --set patroni.securityContext.fsGroup=26 \ --set GLOBAL_SECURITY_CONTEXT="false" \ --set createCredentials=false \ - --set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG} \ + $TESTS_ATP_ARGS \ pgskipper-patroni ./operator/charts/patroni-core echo "State after patroni-core install (diagnostics)..." diff --git a/operator/charts/patroni-core/templates/cr.yaml b/operator/charts/patroni-core/templates/cr.yaml index dbedf375..c4615582 100644 --- a/operator/charts/patroni-core/templates/cr.yaml +++ b/operator/charts/patroni-core/templates/cr.yaml @@ -249,6 +249,48 @@ spec: {{- end }} {{ end }} pgNodeQty: {{ ( include "postgres.replicasCount" . ) }} + {{- if or .Values.tests.environmentName (and .Values.tests.atpStorage .Values.tests.atpStorage.provider) (and .Values.tests.env (len .Values.tests.env | gt 0)) }} + env: + {{- if .Values.tests.environmentName }} + - name: ENVIRONMENT_NAME + value: {{ .Values.tests.environmentName | quote }} + {{- end }} + {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.provider }} + - name: ATP_STORAGE_PROVIDER + value: {{ .Values.tests.atpStorage.provider | quote }} + {{- end }} + {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.serverUrl }} + - name: ATP_STORAGE_SERVER_URL + value: {{ .Values.tests.atpStorage.serverUrl | quote }} + {{- end }} + {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.serverUiUrl }} + - name: ATP_STORAGE_SERVER_UI_URL + value: {{ .Values.tests.atpStorage.serverUiUrl | quote }} + {{- end }} + {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.bucket }} + - name: ATP_STORAGE_BUCKET + value: {{ .Values.tests.atpStorage.bucket | quote }} + {{- end }} + {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.region }} + - name: ATP_STORAGE_REGION + value: {{ .Values.tests.atpStorage.region | quote }} + {{- end }} + {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.username }} + - name: ATP_STORAGE_USERNAME + value: {{ .Values.tests.atpStorage.username | quote }} + {{- end }} + {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.password }} + - name: ATP_STORAGE_PASSWORD + value: {{ .Values.tests.atpStorage.password | quote }} + {{- end }} + {{- if .Values.tests.atpReportViewUiUrl }} + - name: ATP_REPORT_VIEW_UI_URL + value: {{ .Values.tests.atpReportViewUiUrl | quote }} + {{- end }} + {{- if .Values.tests.env }} + {{- toYaml .Values.tests.env | nindent 4 }} + {{- end }} + {{- end }} {{ end }} {{ if .Values.runTestsOnly }} runTestsTime: {{ now | unixEpoch | quote }} From 0976fb88b6877670f20812367b8676bf4930cb41 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 18:12:35 +0500 Subject: [PATCH 24/37] fix --- operator/charts/patroni-core/templates/cr.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/operator/charts/patroni-core/templates/cr.yaml b/operator/charts/patroni-core/templates/cr.yaml index c4615582..bee73025 100644 --- a/operator/charts/patroni-core/templates/cr.yaml +++ b/operator/charts/patroni-core/templates/cr.yaml @@ -249,7 +249,7 @@ spec: {{- end }} {{ end }} pgNodeQty: {{ ( include "postgres.replicasCount" . ) }} - {{- if or .Values.tests.environmentName (and .Values.tests.atpStorage .Values.tests.atpStorage.provider) (and .Values.tests.env (len .Values.tests.env | gt 0)) }} + {{- if or .Values.tests.environmentName (and .Values.tests.atpStorage .Values.tests.atpStorage.provider) (gt (len (.Values.tests.env | default list)) 0) }} env: {{- if .Values.tests.environmentName }} - name: ENVIRONMENT_NAME From 65834bc2821da1041857c59888096d5ab654b9d3 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 18:30:54 +0500 Subject: [PATCH 25/37] add vars --- .github/workflows/install_pgskipper_to_aws.yaml | 2 ++ operator/charts/patroni-core/values.yaml | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index c6f007a1..92ac6e61 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -201,6 +201,8 @@ jobs: # patroni.fsGroup=26 so data volume is writable by postgres user (GID 26), avoids "Permission denied" on /var/lib/pgsql/data. echo "--- Chart CR manifest (patroni.securityContext must appear) ---" helm template pgskipper-patroni ./operator/charts/patroni-core -f ./operator/charts/patroni-core/values.yaml --set patroni.fsGroup=26 --set createCredentials=false -s templates/cr.yaml 2>/dev/null | grep -A3 securityContext || echo "(no securityContext in rendered CR - check cr.yaml and values)" + echo "--- PatroniCore integrationTests (env here will be in the test pod; if empty, set ATP/S3 vars in Environment) ---" + helm template pgskipper-patroni ./operator/charts/patroni-core -f ./operator/charts/patroni-core/values.yaml --set patroni.fsGroup=26 --set createCredentials=false $TESTS_ATP_ARGS -s templates/cr.yaml 2>/dev/null | grep -A200 "integrationTests:" | head -120 echo "Installing patroni-core chart (PatroniCore CR + patroni-core-operator)..." helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ --create-namespace \ diff --git a/operator/charts/patroni-core/values.yaml b/operator/charts/patroni-core/values.yaml index 5b095814..7e5cdbed 100644 --- a/operator/charts/patroni-core/values.yaml +++ b/operator/charts/patroni-core/values.yaml @@ -275,6 +275,18 @@ tests: install: true dockerImage: ghcr.io/netcracker/pgskipper-operator-tests:main podLabels: {} + # S3/ATP storage for test results (defaults; override from GitHub vars/secrets in workflow) + atpStorage: + provider: "aws" + serverUrl: "" + serverUiUrl: "" + bucket: "" + region: "us-east-1" + username: "" + password: "" + atpReportViewUiUrl: "" + environmentName: "" + env: [] # One of "full", "basic"or one from testScenarios runTestScenarios: "basic" testScenarios: From 87b9e334b3e1532e48891385a12be36e743c6b7d Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 18:58:39 +0500 Subject: [PATCH 26/37] adapt new scripts --- .../crds/netcracker.com_patronicores.yaml | 14 ++++++++++++++ tests/Dockerfile | 16 ++++++++++++---- tests/docker/pgskipper-robot-entrypoint.sh | 5 +++++ 3 files changed, 31 insertions(+), 4 deletions(-) create mode 100644 tests/docker/pgskipper-robot-entrypoint.sh diff --git a/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml b/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml index 2d37f6ca..34b16ecd 100644 --- a/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml +++ b/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml @@ -1093,6 +1093,20 @@ spec: items: type: string type: array + env: + description: List of environment variables for the integration test pod (e.g. ATP_STORAGE_*) + items: + type: object + required: + - name + properties: + name: + type: string + value: + type: string + valueFrom: + type: object + type: array type: object ldap: properties: diff --git a/tests/Dockerfile b/tests/Dockerfile index 01d31589..e74fa1e3 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -17,15 +17,22 @@ RUN chmod -R g=u /etc/passwd && \ mkdir /app && \ mkdir /test_runs +# Project structure (unchanged): app and robot under /app, /test_runs COPY ./app/* /app/ COPY robot /test_runs/ +# Also expose tests under base image layout so new scripts (adapter-S3, etc.) can run +COPY robot ${ROBOT_HOME}/tests + COPY docker/uid_entrypoint /opt/uid_entrypoint +# Wrapper: set TAGS from TESTS_TAGS then run base entrypoint (adapter-S3, run-robot) +COPY docker/pgskipper-robot-entrypoint.sh /opt/pgskipper-robot-entrypoint.sh RUN chgrp -R 0 /app && chmod g+w /app && \ chgrp -R 0 /test_runs && chmod -R g+w /test_runs && \ - chmod +x /opt/uid_entrypoint + chmod +x /opt/uid_entrypoint /opt/pgskipper-robot-entrypoint.sh && \ + chown -R 1000:0 ${ROBOT_HOME} && chmod -R 775 ${ROBOT_HOME} -# Volumes are defined to support read-only root file system +# Volumes as in original (read-only root FS support, etc.) VOLUME /etc VOLUME /app VOLUME /test_runs @@ -34,5 +41,6 @@ VOLUME /tmp USER 1001 WORKDIR /app -ENTRYPOINT [ "/opt/uid_entrypoint" ] -CMD ["robot -i ${TESTS_TAGS} /test_runs/"] +ENTRYPOINT ["/opt/uid_entrypoint"] +# Use new scripts flow (base entrypoint + adapter-S3); tags from TESTS_TAGS env +CMD ["/opt/pgskipper-robot-entrypoint.sh", "run-robot"] diff --git a/tests/docker/pgskipper-robot-entrypoint.sh b/tests/docker/pgskipper-robot-entrypoint.sh new file mode 100644 index 00000000..ccc4d69f --- /dev/null +++ b/tests/docker/pgskipper-robot-entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/bash +# Bridge TESTS_TAGS (set by operator) to TAGS (expected by base image entrypoint). +# Ensures base image flow (adapter-S3, output, etc.) runs with correct tags. +export TAGS="${TESTS_TAGS:-$TAGS}" +exec /docker-entrypoint.sh "$@" From bfe054a30f5a6173744abcc11241c8ace864d82b Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 19:12:02 +0500 Subject: [PATCH 27/37] for new scripts --- operator/pkg/deployment/tests.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/operator/pkg/deployment/tests.go b/operator/pkg/deployment/tests.go index 53153efa..ae4a5ccc 100644 --- a/operator/pkg/deployment/tests.go +++ b/operator/pkg/deployment/tests.go @@ -75,7 +75,6 @@ func NewIntegrationTestsPod(cr *v1.PatroniServices, cluster *patroniv1.PatroniCl Image: dockerImage, ImagePullPolicy: cr.Spec.ImagePullPolicy, SecurityContext: util.GetDefaultSecurityContext(), - Args: []string{"robot", "-i", tastsTags, "/test_runs/"}, Env: []corev1.EnvVar{ { Name: "POSTGRES_USER", @@ -195,7 +194,6 @@ func NewCoreIntegrationTests(cr *patroniv1.PatroniCore, cluster *patroniv1.Patro Image: dockerImage, ImagePullPolicy: cr.Spec.ImagePullPolicy, SecurityContext: util.GetDefaultSecurityContext(), - Args: []string{"robot", "-i", tastsTags, "/test_runs/"}, Env: []corev1.EnvVar{ { Name: "POSTGRES_USER", From 10256a93bbe823cdece2cd4a9b7d0251336cbe7d Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 19:27:15 +0500 Subject: [PATCH 28/37] for tests --- tests/docker/pgskipper-robot-entrypoint.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/docker/pgskipper-robot-entrypoint.sh b/tests/docker/pgskipper-robot-entrypoint.sh index ccc4d69f..0970e7e3 100644 --- a/tests/docker/pgskipper-robot-entrypoint.sh +++ b/tests/docker/pgskipper-robot-entrypoint.sh @@ -1,5 +1,6 @@ #!/bin/bash # Bridge TESTS_TAGS (set by operator) to TAGS (expected by base image entrypoint). -# Ensures base image flow (adapter-S3, output, etc.) runs with correct tags. export TAGS="${TESTS_TAGS:-$TAGS}" +# Base scripts use ./tests and ./output relative to cwd; image has WORKDIR=/app but tests live in ROBOT_HOME/tests +cd "${ROBOT_HOME:-/opt/robot}" || exit 1 exec /docker-entrypoint.sh "$@" From b92945ab94a8c2a6be4e22a929f15804b557f2b6 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Wed, 25 Feb 2026 19:32:52 +0500 Subject: [PATCH 29/37] fix vars --- .github/workflows/install_pgskipper_to_aws.yaml | 13 ++++++++++--- operator/controllers/patroni_core_controller.go | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 92ac6e61..cf3eef72 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -168,15 +168,20 @@ jobs: ENV_NAME="${{ vars.ENVIRONMENT_NAME }}" [ -z "$ENV_NAME" ] && ENV_NAME="${{ github.event.inputs.environment }}" - # Tests image + ATP/S3 storage for test results (used by both patroni-services and patroni-core) + AWS_REGION="${{ vars.AWS_REGION }}" + [ -z "$AWS_REGION" ] && AWS_REGION="us-east-1" + ATP_PROVIDER="${{ vars.ATP_STORAGE_PROVIDER }}" + [ -z "$ATP_PROVIDER" ] && ATP_PROVIDER="aws" + # Tests image + ATP/S3 storage for test results (used by both patroni-services and patroni-core). + # Always set provider and region so integrationTests.env is never empty (chart needs at least one of env/environmentName/atpStorage.provider). TESTS_ATP_ARGS="--set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" + TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.provider=${ATP_PROVIDER}" + TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.region=${AWS_REGION}" [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" [ -n "${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.password=${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" - [ -n "${{ vars.ATP_STORAGE_PROVIDER }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.provider=${{ vars.ATP_STORAGE_PROVIDER }}" [ -n "${{ vars.ATP_STORAGE_SERVER_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUrl=${{ vars.ATP_STORAGE_SERVER_URL }}" [ -n "${{ vars.ATP_STORAGE_SERVER_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUiUrl=${{ vars.ATP_STORAGE_SERVER_UI_URL }}" [ -n "${{ vars.ATP_STORAGE_BUCKET }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.bucket=${{ vars.ATP_STORAGE_BUCKET }}" - [ -n "${{ vars.AWS_REGION }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.region=${{ vars.AWS_REGION }}" [ -n "${{ vars.ATP_REPORT_VIEW_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpReportViewUiUrl=${{ vars.ATP_REPORT_VIEW_UI_URL }}" [ -n "$ENV_NAME" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.environmentName=$ENV_NAME" @@ -221,6 +226,8 @@ jobs: echo "State after patroni-core install (diagnostics)..." echo "--- Checkout ref (chart source): ${{ github.ref }} ${{ github.sha }} ---" + echo "--- PatroniCore spec.integrationTests (env here must appear in test pod) ---" + kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | grep -A300 "integrationTests:" | head -120 || echo "(no integrationTests in CR)" echo "--- PatroniCore spec.patroni (securityContext must have fsGroup: 26) ---" kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.items[0].spec.patroni}' 2>/dev/null | python3 -m json.tool 2>/dev/null || kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | head -80 echo "--- StatefulSet pod template securityContext ---" diff --git a/operator/controllers/patroni_core_controller.go b/operator/controllers/patroni_core_controller.go index fd141d76..137af266 100644 --- a/operator/controllers/patroni_core_controller.go +++ b/operator/controllers/patroni_core_controller.go @@ -460,6 +460,9 @@ func (pr *PatroniCoreReconciler) AddExcludeLabelToCm(c client.Client, cmName str func (pr *PatroniCoreReconciler) createTestsPods(cr *qubershipv1.PatroniCore) error { if cr.Spec.IntegrationTests != nil { + if n := len(cr.Spec.IntegrationTests.Env); n > 0 { + pr.logger.Info("CR has integrationTests.env, passing to test pod", zap.Int("envCount", n)) + } integrationTestsPod := deployment.NewCoreIntegrationTests(cr, utils.GetPatroniClusterSettings(cr.Spec.Patroni.ClusterName)) state, err := utils.GetPodPhase(integrationTestsPod) if err != nil { From 6b0078e5b98a1875b911ef1bc61a606010cf9544 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Thu, 26 Feb 2026 12:50:39 +0500 Subject: [PATCH 30/37] fix variables --- .../upgrade_patroni_core_env_to_aws.yaml | 98 +++++++++++++++++++ .../charts/patroni-core/templates/cr.yaml | 35 ++++--- 2 files changed, 115 insertions(+), 18 deletions(-) create mode 100644 .github/workflows/upgrade_patroni_core_env_to_aws.yaml diff --git a/.github/workflows/upgrade_patroni_core_env_to_aws.yaml b/.github/workflows/upgrade_patroni_core_env_to_aws.yaml new file mode 100644 index 00000000..2c3de52d --- /dev/null +++ b/.github/workflows/upgrade_patroni_core_env_to_aws.yaml @@ -0,0 +1,98 @@ +# Upgrade only the patroni-core Helm release to inject integrationTests.env into the CR. +# Use when the CR was installed without ATP/S3 env (e.g. before workflow fix) and you don't want a full reinstall. +name: Upgrade PatroniCore env to AWS + +on: + workflow_dispatch: + inputs: + environment: + description: "Environment Name (must have vars/secrets set)" + required: false + default: "dev" + repository: + description: "Repo to checkout. Empty = this repo." + required: false + default: "" + ref: + description: "Branch or ref to checkout" + required: false + default: "main" + +jobs: + Upgrade-PatroniCore-Env: + environment: "${{ github.event.inputs.environment }}" + runs-on: ubuntu-latest + + steps: + - name: Checkout Code + uses: actions/checkout@v4 + with: + repository: ${{ github.event.inputs.repository || github.repository }} + ref: ${{ github.event.inputs.ref || github.ref }} + + - name: Validate required Environment variables + run: | + MISSING="" + [ -z "${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" ] && MISSING="${MISSING} PGSKIPPER_INSTALL_NAMESPACE" + [ -z "${{ vars.AWS_CLUSTERNAME }}" ] && MISSING="${MISSING} AWS_CLUSTERNAME" + [ -z "${{ secrets.AWS_ACCESS_KEY_ID }}" ] && MISSING="${MISSING} AWS_ACCESS_KEY_ID" + [ -z "${{ secrets.AWS_SECRET_ACCESS_KEY }}" ] && MISSING="${MISSING} AWS_SECRET_ACCESS_KEY" + if [ -n "$MISSING" ]; then + echo "::error::Missing:$MISSING" + exit 1 + fi + + - name: Setup kubectl + uses: azure/setup-kubectl@v3 + with: + version: 'v1.32.0' + + - name: Setup Helm + uses: azure/setup-helm@v3 + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: ${{ vars.AWS_REGION || 'us-east-1' }} + + - name: Configure kubeconfig for EKS + run: | + aws eks update-kubeconfig --region ${{ vars.AWS_REGION || 'us-east-1' }} --name ${{ vars.AWS_CLUSTERNAME }} + + - name: Upgrade patroni-core (inject integrationTests.env) + run: | + NS="${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" + GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') + IMAGE_TAG=$(echo ${{ github.ref }} | sed 's@refs/tags/@@;s@refs/heads/@@;s@/@_@g') + ENV_NAME="${{ vars.ENVIRONMENT_NAME }}" + [ -z "$ENV_NAME" ] && ENV_NAME="${{ github.event.inputs.environment }}" + AWS_REGION="${{ vars.AWS_REGION }}" + [ -z "$AWS_REGION" ] && AWS_REGION="us-east-1" + ATP_PROVIDER="${{ vars.ATP_STORAGE_PROVIDER }}" + [ -z "$ATP_PROVIDER" ] && ATP_PROVIDER="aws" + + TESTS_ATP_ARGS="--set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" + TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.provider=${ATP_PROVIDER}" + TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.region=${AWS_REGION}" + [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" + [ -n "${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.password=${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" + [ -n "${{ vars.ATP_STORAGE_SERVER_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUrl=${{ vars.ATP_STORAGE_SERVER_URL }}" + [ -n "${{ vars.ATP_STORAGE_SERVER_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUiUrl=${{ vars.ATP_STORAGE_SERVER_UI_URL }}" + [ -n "${{ vars.ATP_STORAGE_BUCKET }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.bucket=${{ vars.ATP_STORAGE_BUCKET }}" + [ -n "${{ vars.ATP_REPORT_VIEW_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpReportViewUiUrl=${{ vars.ATP_REPORT_VIEW_UI_URL }}" + [ -n "$ENV_NAME" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.environmentName=$ENV_NAME" + + echo "Upgrading pgskipper-patroni in $NS with TESTS_ATP_ARGS (env will be in CR)..." + # -f values.yaml ensures chart defaults (tests.atpStorage, etc.) are in the merge; --reuse-values then overlay; --set overrides. + helm upgrade --namespace="$NS" --install \ + -f ./operator/charts/patroni-core/values.yaml \ + --reuse-values \ + $TESTS_ATP_ARGS \ + pgskipper-patroni ./operator/charts/patroni-core + + echo "--- PatroniCore spec.integrationTests (env should appear below) ---" + kubectl get patronicore -n "$NS" -o yaml | grep -A200 "integrationTests:" | head -80 + + echo "Delete the test pod so the operator recreates it with env: kubectl delete pod integration-robot-tests -n $NS" diff --git a/operator/charts/patroni-core/templates/cr.yaml b/operator/charts/patroni-core/templates/cr.yaml index bee73025..19577e85 100644 --- a/operator/charts/patroni-core/templates/cr.yaml +++ b/operator/charts/patroni-core/templates/cr.yaml @@ -249,39 +249,38 @@ spec: {{- end }} {{ end }} pgNodeQty: {{ ( include "postgres.replicasCount" . ) }} - {{- if or .Values.tests.environmentName (and .Values.tests.atpStorage .Values.tests.atpStorage.provider) (gt (len (.Values.tests.env | default list)) 0) }} + {{- $atp := .Values.tests.atpStorage | default dict }} + {{- if or .Values.tests.environmentName .Values.tests.atpStorage .Values.tests.atpReportViewUiUrl (gt (len (.Values.tests.env | default list)) 0) }} env: {{- if .Values.tests.environmentName }} - name: ENVIRONMENT_NAME value: {{ .Values.tests.environmentName | quote }} {{- end }} - {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.provider }} + {{- if .Values.tests.atpStorage }} - name: ATP_STORAGE_PROVIDER - value: {{ .Values.tests.atpStorage.provider | quote }} - {{- end }} - {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.serverUrl }} + value: {{ ($atp.provider | default "aws") | quote }} + - name: ATP_STORAGE_REGION + value: {{ ($atp.region | default "us-east-1") | quote }} + {{- if $atp.serverUrl }} - name: ATP_STORAGE_SERVER_URL - value: {{ .Values.tests.atpStorage.serverUrl | quote }} + value: {{ $atp.serverUrl | quote }} {{- end }} - {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.serverUiUrl }} + {{- if $atp.serverUiUrl }} - name: ATP_STORAGE_SERVER_UI_URL - value: {{ .Values.tests.atpStorage.serverUiUrl | quote }} + value: {{ $atp.serverUiUrl | quote }} {{- end }} - {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.bucket }} + {{- if $atp.bucket }} - name: ATP_STORAGE_BUCKET - value: {{ .Values.tests.atpStorage.bucket | quote }} + value: {{ $atp.bucket | quote }} {{- end }} - {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.region }} - - name: ATP_STORAGE_REGION - value: {{ .Values.tests.atpStorage.region | quote }} - {{- end }} - {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.username }} + {{- if $atp.username }} - name: ATP_STORAGE_USERNAME - value: {{ .Values.tests.atpStorage.username | quote }} + value: {{ $atp.username | quote }} {{- end }} - {{- if and .Values.tests.atpStorage .Values.tests.atpStorage.password }} + {{- if $atp.password }} - name: ATP_STORAGE_PASSWORD - value: {{ .Values.tests.atpStorage.password | quote }} + value: {{ $atp.password | quote }} + {{- end }} {{- end }} {{- if .Values.tests.atpReportViewUiUrl }} - name: ATP_REPORT_VIEW_UI_URL From bc083bf4dbb4a7dc875540a8c51c81ca5eef5c03 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Thu, 26 Feb 2026 13:19:59 +0500 Subject: [PATCH 31/37] fix vars --- operator/charts/patroni-core/templates/cr.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/operator/charts/patroni-core/templates/cr.yaml b/operator/charts/patroni-core/templates/cr.yaml index 19577e85..2312d0a1 100644 --- a/operator/charts/patroni-core/templates/cr.yaml +++ b/operator/charts/patroni-core/templates/cr.yaml @@ -250,13 +250,11 @@ spec: {{ end }} pgNodeQty: {{ ( include "postgres.replicasCount" . ) }} {{- $atp := .Values.tests.atpStorage | default dict }} - {{- if or .Values.tests.environmentName .Values.tests.atpStorage .Values.tests.atpReportViewUiUrl (gt (len (.Values.tests.env | default list)) 0) }} env: {{- if .Values.tests.environmentName }} - name: ENVIRONMENT_NAME value: {{ .Values.tests.environmentName | quote }} {{- end }} - {{- if .Values.tests.atpStorage }} - name: ATP_STORAGE_PROVIDER value: {{ ($atp.provider | default "aws") | quote }} - name: ATP_STORAGE_REGION @@ -281,7 +279,6 @@ spec: - name: ATP_STORAGE_PASSWORD value: {{ $atp.password | quote }} {{- end }} - {{- end }} {{- if .Values.tests.atpReportViewUiUrl }} - name: ATP_REPORT_VIEW_UI_URL value: {{ .Values.tests.atpReportViewUiUrl | quote }} @@ -289,7 +286,6 @@ spec: {{- if .Values.tests.env }} {{- toYaml .Values.tests.env | nindent 4 }} {{- end }} - {{- end }} {{ end }} {{ if .Values.runTestsOnly }} runTestsTime: {{ now | unixEpoch | quote }} From 72c06df6c9989cce2a172829391fa4d9d07c55f1 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Thu, 26 Feb 2026 13:21:52 +0500 Subject: [PATCH 32/37] trigger --- operator/charts/patroni-services/values.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/operator/charts/patroni-services/values.yaml b/operator/charts/patroni-services/values.yaml index 793589d4..498251a0 100644 --- a/operator/charts/patroni-services/values.yaml +++ b/operator/charts/patroni-services/values.yaml @@ -487,3 +487,4 @@ CLOUD_PUBLIC_HOST: "k8s.default" # DBAAS_CLUSTER_DBA_CREDENTIALS_PASSWORD: "user-2" # MONITORING_ENABLED: false # INFRA_POSTGRES_FS_GROUP: 26 + From cd2ec49b563d83137cc13876cb506f1c23953633 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Thu, 26 Feb 2026 13:37:39 +0500 Subject: [PATCH 33/37] apply crd --- .github/workflows/install_pgskipper_to_aws.yaml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index cf3eef72..26c399ab 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -204,6 +204,9 @@ jobs: # Install patroni-core so Patroni pods and Service pg-patroni are created (by operator with OPERATOR_ROLE=patroni). # Without this, only PatroniServices controller runs and backup daemon has no Postgres to connect to. # patroni.fsGroup=26 so data volume is writable by postgres user (GID 26), avoids "Permission denied" on /var/lib/pgsql/data. + # Apply CRD so spec.integrationTests.env is accepted; otherwise API strips env and the test pod gets no ATP_* vars. + echo "Applying PatroniCore CRD (required for integrationTests.env)..." + kubectl apply -f ./operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml echo "--- Chart CR manifest (patroni.securityContext must appear) ---" helm template pgskipper-patroni ./operator/charts/patroni-core -f ./operator/charts/patroni-core/values.yaml --set patroni.fsGroup=26 --set createCredentials=false -s templates/cr.yaml 2>/dev/null | grep -A3 securityContext || echo "(no securityContext in rendered CR - check cr.yaml and values)" echo "--- PatroniCore integrationTests (env here will be in the test pod; if empty, set ATP/S3 vars in Environment) ---" @@ -228,6 +231,8 @@ jobs: echo "--- Checkout ref (chart source): ${{ github.ref }} ${{ github.sha }} ---" echo "--- PatroniCore spec.integrationTests (env here must appear in test pod) ---" kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | grep -A300 "integrationTests:" | head -120 || echo "(no integrationTests in CR)" + echo "Deleting integration test pod so operator recreates it with env from CR..." + kubectl delete pod integration-robot-tests -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --ignore-not-found=true --wait=false || true echo "--- PatroniCore spec.patroni (securityContext must have fsGroup: 26) ---" kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.items[0].spec.patroni}' 2>/dev/null | python3 -m json.tool 2>/dev/null || kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | head -80 echo "--- StatefulSet pod template securityContext ---" From c66ab60d3a23c322df890bf44285dcfde7d70dd1 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Thu, 26 Feb 2026 14:56:29 +0500 Subject: [PATCH 34/37] fix test tags --- .../workflows/install_pgskipper_to_aws.yaml | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml index 26c399ab..1ea1d2eb 100644 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ b/.github/workflows/install_pgskipper_to_aws.yaml @@ -8,17 +8,9 @@ on: required: false default: "dev" test_scenario: - description: "Test scenario (e.g. basic, patroniBasic, or empty for default)" + description: "Test scenario (e.g. basic, full, patroniBasic)" required: false default: "basic" - repository: - description: "Repo to checkout (e.g. owner/pgskipper-operator-demo). Empty = this repo (use when running from your fork)." - required: false - default: "" - ref: - description: "Branch or ref to checkout (e.g. main). Used only if repository is set." - required: false - default: "main" jobs: Install-Pgskipper: @@ -28,9 +20,6 @@ jobs: steps: - name: Checkout Code uses: actions/checkout@v4 - with: - repository: ${{ github.event.inputs.repository || github.repository }} - ref: ${{ github.event.inputs.ref || github.ref }} - name: Validate required Environment variables run: | @@ -174,7 +163,10 @@ jobs: [ -z "$ATP_PROVIDER" ] && ATP_PROVIDER="aws" # Tests image + ATP/S3 storage for test results (used by both patroni-services and patroni-core). # Always set provider and region so integrationTests.env is never empty (chart needs at least one of env/environmentName/atpStorage.provider). + TEST_SCENARIO="${{ github.event.inputs.test_scenario }}" + [ -z "$TEST_SCENARIO" ] && TEST_SCENARIO="basic" TESTS_ATP_ARGS="--set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" + TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.runTestScenarios=${TEST_SCENARIO}" TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.provider=${ATP_PROVIDER}" TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.region=${AWS_REGION}" [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" @@ -189,7 +181,7 @@ jobs: HELM_ARGS="$HELM_ARGS --set operator.image=ghcr.io/${GITHUB_GROUP}/pgskipper-operator:${IMAGE_TAG}" HELM_ARGS="$HELM_ARGS --set metricCollector.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-monitoring-agent:${IMAGE_TAG}" HELM_ARGS="$HELM_ARGS --set backupDaemon.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-backup-daemon:${IMAGE_TAG}" - HELM_ARGS="$HELM_ARGS --set tests.runTestScenarios=${{ github.event.inputs.test_scenario || 'basic' }}" + HELM_ARGS="$HELM_ARGS --set tests.runTestScenarios=${TEST_SCENARIO}" HELM_ARGS="$HELM_ARGS $TESTS_ATP_ARGS" echo "Helm additional args: $HELM_ARGS" @@ -228,8 +220,9 @@ jobs: pgskipper-patroni ./operator/charts/patroni-core echo "State after patroni-core install (diagnostics)..." + echo "--- Test scenario: ${TEST_SCENARIO} → CR runTestScenarios → pod TESTS_TAGS (full→patroni*, basic→patroni_basic) ---" echo "--- Checkout ref (chart source): ${{ github.ref }} ${{ github.sha }} ---" - echo "--- PatroniCore spec.integrationTests (env here must appear in test pod) ---" + echo "--- PatroniCore spec.integrationTests (runTestScenarios + env; operator maps to pod TESTS_TAGS) ---" kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | grep -A300 "integrationTests:" | head -120 || echo "(no integrationTests in CR)" echo "Deleting integration test pod so operator recreates it with env from CR..." kubectl delete pod integration-robot-tests -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --ignore-not-found=true --wait=false || true From 82f8089022148a5fe844e93ad279d0437112aa9f Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 27 Mar 2026 17:02:15 +0500 Subject: [PATCH 35/37] prepare for PR --- tests/Dockerfile | 3 ++- tests/docker/requirements.txt | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/Dockerfile b/tests/Dockerfile index e74fa1e3..537bfce0 100644 --- a/tests/Dockerfile +++ b/tests/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/elbe0116/qubership-docker-integration-tests:PSUPATPOS-63 +FROM ghcr.io/netcracker/qubership-docker-integration-tests:main ENV LC_ALL=en_US.UTF-8 \ LANG=en_US.UTF-8 @@ -10,6 +10,7 @@ COPY docker/pip.conf /root/.pip/pip.conf COPY docker/requirements.txt /root/requirements.txt RUN set -x \ + && apk add --update --no-cache build-base postgresql-dev \ && pip3 install --no-cache-dir -r /root/requirements.txt \ && pip3 uninstall -y pip diff --git a/tests/docker/requirements.txt b/tests/docker/requirements.txt index 9cf8b805..ac76eb16 100644 --- a/tests/docker/requirements.txt +++ b/tests/docker/requirements.txt @@ -17,8 +17,8 @@ Jinja2==3.1.6 kubernetes==34.1.0 MarkupSafe==3.0.3 oauthlib==3.3.1 -# Binary build — no build-base/postgresql-dev needed (was psycopg2==2.9.10) -psycopg2-binary==2.9.10 +# Binary build — no build-base/postgresql-dev needed if aws (psycopg2-binary==2.9.10) +psycopg2==2.9.10 pyasn1==0.6.3 pyasn1-modules==0.4.2 # Match base image (was 2.4.0) From 39a4d587aaaadaa474978675eb66aadcbf2c4e0c Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 27 Mar 2026 17:04:51 +0500 Subject: [PATCH 36/37] prepare for PR --- operator/charts/patroni-core/values.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/operator/charts/patroni-core/values.yaml b/operator/charts/patroni-core/values.yaml index ff303b14..c7db8376 100644 --- a/operator/charts/patroni-core/values.yaml +++ b/operator/charts/patroni-core/values.yaml @@ -278,14 +278,14 @@ tests: # S3/ATP storage for test results (defaults; override from GitHub vars/secrets in workflow) atpStorage: provider: "aws" - serverUrl: "" - serverUiUrl: "" + serverUrl: "https://s3.amazonaws.com" + serverUiUrl: "https://console.test.com" bucket: "" region: "us-east-1" username: "" password: "" - atpReportViewUiUrl: "" - environmentName: "" + atpReportViewUiUrl: "https://test.com" + environmentName: "pgskipper-operator" env: [] # One of "full", "basic"or one from testScenarios runTestScenarios: "basic" From 7b6382586fb463daf093aaa2b4f98b2c5df8d537 Mon Sep 17 00:00:00 2001 From: elbe0116 Date: Fri, 27 Mar 2026 17:29:08 +0500 Subject: [PATCH 37/37] prepare for PR --- .../workflows/install_pgskipper_to_aws.yaml | 258 ------------------ .../upgrade_patroni_core_env_to_aws.yaml | 10 +- README.md | 16 ++ operator/api/apps/v1/postgresservice_types.go | 1 - operator/api/apps/v1/zz_generated.deepcopy.go | 7 - .../api/common/v1/zz_generated.deepcopy.go | 2 + operator/api/patroni/v1/patronicore_types.go | 1 - .../api/patroni/v1/zz_generated.deepcopy.go | 7 - .../crds/netcracker.com_patronicores.yaml | 17 +- .../charts/patroni-core/templates/cr.yaml | 3 - operator/charts/patroni-core/values.yaml | 3 +- .../crds/netcracker.com_patroniservices.yaml | 157 ----------- .../charts/patroni-services/templates/cr.yaml | 7 +- operator/charts/patroni-services/values.yaml | 2 - .../controllers/patroni_core_controller.go | 3 - operator/pkg/deployment/tests.go | 6 - tests/docker/requirements.txt | 2 +- 17 files changed, 29 insertions(+), 473 deletions(-) delete mode 100644 .github/workflows/install_pgskipper_to_aws.yaml diff --git a/.github/workflows/install_pgskipper_to_aws.yaml b/.github/workflows/install_pgskipper_to_aws.yaml deleted file mode 100644 index 1ea1d2eb..00000000 --- a/.github/workflows/install_pgskipper_to_aws.yaml +++ /dev/null @@ -1,258 +0,0 @@ -name: Install Pgskipper to AWS - -on: - workflow_dispatch: - inputs: - environment: - description: "Environment Name" - required: false - default: "dev" - test_scenario: - description: "Test scenario (e.g. basic, full, patroniBasic)" - required: false - default: "basic" - -jobs: - Install-Pgskipper: - environment: "${{ github.event.inputs.environment }}" - runs-on: ubuntu-latest - - steps: - - name: Checkout Code - uses: actions/checkout@v4 - - - name: Validate required Environment variables - run: | - MISSING="" - [ -z "${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" ] && MISSING="${MISSING} PGSKIPPER_INSTALL_NAMESPACE" - [ -z "${{ vars.AWS_CLUSTERNAME }}" ] && MISSING="${MISSING} AWS_CLUSTERNAME" - [ -z "${{ secrets.AWS_ACCESS_KEY_ID }}" ] && MISSING="${MISSING} AWS_ACCESS_KEY_ID" - [ -z "${{ secrets.AWS_SECRET_ACCESS_KEY }}" ] && MISSING="${MISSING} AWS_SECRET_ACCESS_KEY" - if [ -n "$MISSING" ]; then - echo "::error::Missing required configuration in Environment '${{ github.event.inputs.environment }}'. Set the following Variables (and Secrets):$MISSING" - echo "In GitHub: Settings → Environments → ${{ github.event.inputs.environment }} → add Variables: PGSKIPPER_INSTALL_NAMESPACE, AWS_CLUSTERNAME; Secrets: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY. Optionally AWS_REGION (default us-east-1)." - exit 1 - fi - echo "PGSKIPPER_INSTALL_NAMESPACE=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" - echo "AWS_CLUSTERNAME=${{ vars.AWS_CLUSTERNAME }}" - echo "AWS_REGION=${{ vars.AWS_REGION || 'us-east-1' }}" - - - name: Listing downloaded files - run: | - pwd - ls -R ./ - - - name: Setup kubectl - uses: azure/setup-kubectl@v3 - with: - version: 'v1.32.0' - - - name: Setup Helm - uses: azure/setup-helm@v3 - - - name: Verify tools - run: | - kubectl version --client - helm version - aws --version - - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} - aws-region: ${{ vars.AWS_REGION || 'us-east-1' }} - - - name: Configure kubeconfig for EKS - run: | - aws eks update-kubeconfig --region ${{ vars.AWS_REGION || 'us-east-1' }} --name ${{ vars.AWS_CLUSTERNAME }} - - - name: Verify cluster access - run: kubectl get nodes - - - name: Install Pgskipper (Patroni Services) - run: | - # Uninstall Helm releases first (cleans CRs and avoids finalizers blocking namespace deletion) - echo "Uninstalling Helm releases..." - helm uninstall pgskipper-patroni -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --wait --timeout=120s || true - helm uninstall pgskipper -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --wait --timeout=120s || true - - echo "Deleting namespace..." - kubectl delete ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=180s --wait=false || true - - echo "Waiting for namespace deletion to complete..." - for i in $(seq 1 90); do - if ! kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null; then - echo "Namespace deleted successfully" - break - fi - - NS_STATUS=$(kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.status.phase}' 2>/dev/null || echo "") - echo "Waiting... ($i/90) - Namespace status: $NS_STATUS" - - if [ "$i" -gt 30 ] && [ "$NS_STATUS" = "Terminating" ]; then - echo "Namespace stuck in Terminating, attempting force delete..." - kubectl api-resources --verbs=list --namespaced -o name | xargs -n 1 kubectl get --show-kind --ignore-not-found -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o name 2>/dev/null | \ - xargs -n 1 kubectl patch -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -p '{"metadata":{"finalizers":[]}}' --type=merge 2>/dev/null || true - kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o json | \ - jq '.spec.finalizers = []' | \ - kubectl replace --raw "/api/v1/namespaces/${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}/finalize" -f - || true - sleep 5 - fi - - sleep 2 - done - - if kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null; then - echo "ERROR: Namespace still exists after timeout!" - kubectl get ns ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml - exit 1 - fi - - echo "Creating namespace: ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" - kubectl create namespace ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} - - # Operator requires these secrets; chart only creates them when externalDataBase is set - echo "Creating postgres-credentials and replicator-credentials secrets..." - kubectl create secret generic postgres-credentials -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ - --from-literal=username=postgres \ - --from-literal=password='p@ssWOrD1' \ - --dry-run=client -o yaml | kubectl apply -f - - kubectl create secret generic replicator-credentials -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ - --from-literal=username=replicator \ - --from-literal=password=replicator \ - --dry-run=client -o yaml | kubectl apply -f - - - # Image registry/tag from the repo that triggered the workflow (your fork = your images) - GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') - IMAGE_TAG=$(echo ${{ github.ref }} | sed 's@refs/tags/@@;s@refs/heads/@@;s@/@_@g') - echo "Using images: ghcr.io/${GITHUB_GROUP}/...:${IMAGE_TAG}" - - # AWS EKS values: TLS off, securityContext, tests, minimal components for integration tests - # GLOBAL_SECURITY_CONTEXT: false so operator does not set runAsNonRoot on containers (backup-daemon image runs as root) - cat << 'VALUESEOF' | sed 's/^ //' > /tmp/pgskipper-aws-values.yaml - global: - tls: - enabled: false - securityContext: {} - GLOBAL_SECURITY_CONTEXT: "false" - tls: - enabled: false - patroni: - clusterName: patroni - metricCollector: - install: true - securityContext: {} - backupDaemon: - install: true - securityContext: - runAsNonRoot: false - storage: - storageClass: gp2 - tests: - install: true - runTestScenarios: basic - affinity: null - VALUESEOF - - ENV_NAME="${{ vars.ENVIRONMENT_NAME }}" - [ -z "$ENV_NAME" ] && ENV_NAME="${{ github.event.inputs.environment }}" - AWS_REGION="${{ vars.AWS_REGION }}" - [ -z "$AWS_REGION" ] && AWS_REGION="us-east-1" - ATP_PROVIDER="${{ vars.ATP_STORAGE_PROVIDER }}" - [ -z "$ATP_PROVIDER" ] && ATP_PROVIDER="aws" - # Tests image + ATP/S3 storage for test results (used by both patroni-services and patroni-core). - # Always set provider and region so integrationTests.env is never empty (chart needs at least one of env/environmentName/atpStorage.provider). - TEST_SCENARIO="${{ github.event.inputs.test_scenario }}" - [ -z "$TEST_SCENARIO" ] && TEST_SCENARIO="basic" - TESTS_ATP_ARGS="--set tests.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-operator-tests:${IMAGE_TAG}" - TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.runTestScenarios=${TEST_SCENARIO}" - TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.provider=${ATP_PROVIDER}" - TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.region=${AWS_REGION}" - [ -n "${{ secrets.S3_AWS_ACCESS_KEY_ID }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.username=${{ secrets.S3_AWS_ACCESS_KEY_ID }}" - [ -n "${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.password=${{ secrets.S3_AWS_SECRET_ACCESS_KEY }}" - [ -n "${{ vars.ATP_STORAGE_SERVER_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUrl=${{ vars.ATP_STORAGE_SERVER_URL }}" - [ -n "${{ vars.ATP_STORAGE_SERVER_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.serverUiUrl=${{ vars.ATP_STORAGE_SERVER_UI_URL }}" - [ -n "${{ vars.ATP_STORAGE_BUCKET }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpStorage.bucket=${{ vars.ATP_STORAGE_BUCKET }}" - [ -n "${{ vars.ATP_REPORT_VIEW_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpReportViewUiUrl=${{ vars.ATP_REPORT_VIEW_UI_URL }}" - [ -n "$ENV_NAME" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.environmentName=$ENV_NAME" - - HELM_ARGS="" - HELM_ARGS="$HELM_ARGS --set operator.image=ghcr.io/${GITHUB_GROUP}/pgskipper-operator:${IMAGE_TAG}" - HELM_ARGS="$HELM_ARGS --set metricCollector.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-monitoring-agent:${IMAGE_TAG}" - HELM_ARGS="$HELM_ARGS --set backupDaemon.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-backup-daemon:${IMAGE_TAG}" - HELM_ARGS="$HELM_ARGS --set tests.runTestScenarios=${TEST_SCENARIO}" - HELM_ARGS="$HELM_ARGS $TESTS_ATP_ARGS" - - echo "Helm additional args: $HELM_ARGS" - - helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ - --create-namespace \ - -f ./operator/charts/patroni-services/values.yaml \ - -f /tmp/pgskipper-aws-values.yaml \ - pgskipper ./operator/charts/patroni-services \ - $HELM_ARGS - - # Install patroni-core so Patroni pods and Service pg-patroni are created (by operator with OPERATOR_ROLE=patroni). - # Without this, only PatroniServices controller runs and backup daemon has no Postgres to connect to. - # patroni.fsGroup=26 so data volume is writable by postgres user (GID 26), avoids "Permission denied" on /var/lib/pgsql/data. - # Apply CRD so spec.integrationTests.env is accepted; otherwise API strips env and the test pod gets no ATP_* vars. - echo "Applying PatroniCore CRD (required for integrationTests.env)..." - kubectl apply -f ./operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml - echo "--- Chart CR manifest (patroni.securityContext must appear) ---" - helm template pgskipper-patroni ./operator/charts/patroni-core -f ./operator/charts/patroni-core/values.yaml --set patroni.fsGroup=26 --set createCredentials=false -s templates/cr.yaml 2>/dev/null | grep -A3 securityContext || echo "(no securityContext in rendered CR - check cr.yaml and values)" - echo "--- PatroniCore integrationTests (env here will be in the test pod; if empty, set ATP/S3 vars in Environment) ---" - helm template pgskipper-patroni ./operator/charts/patroni-core -f ./operator/charts/patroni-core/values.yaml --set patroni.fsGroup=26 --set createCredentials=false $TESTS_ATP_ARGS -s templates/cr.yaml 2>/dev/null | grep -A200 "integrationTests:" | head -120 - echo "Installing patroni-core chart (PatroniCore CR + patroni-core-operator)..." - helm install --namespace=${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} \ - --create-namespace \ - -f ./operator/charts/patroni-core/values.yaml \ - --set tls.enabled=false \ - --set patroni.clusterName=patroni \ - --set operator.image=ghcr.io/${GITHUB_GROUP}/pgskipper-operator:${IMAGE_TAG} \ - --set patroni.dockerImage=ghcr.io/${GITHUB_GROUP}/pgskipper-docker-patroni-16:${IMAGE_TAG} \ - --set patroni.storage.storageClass=gp2 \ - --set patroni.fsGroup=26 \ - --set patroni.securityContext.fsGroup=26 \ - --set GLOBAL_SECURITY_CONTEXT="false" \ - --set createCredentials=false \ - $TESTS_ATP_ARGS \ - pgskipper-patroni ./operator/charts/patroni-core - - echo "State after patroni-core install (diagnostics)..." - echo "--- Test scenario: ${TEST_SCENARIO} → CR runTestScenarios → pod TESTS_TAGS (full→patroni*, basic→patroni_basic) ---" - echo "--- Checkout ref (chart source): ${{ github.ref }} ${{ github.sha }} ---" - echo "--- PatroniCore spec.integrationTests (runTestScenarios + env; operator maps to pod TESTS_TAGS) ---" - kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | grep -A300 "integrationTests:" | head -120 || echo "(no integrationTests in CR)" - echo "Deleting integration test pod so operator recreates it with env from CR..." - kubectl delete pod integration-robot-tests -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --ignore-not-found=true --wait=false || true - echo "--- PatroniCore spec.patroni (securityContext must have fsGroup: 26) ---" - kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.items[0].spec.patroni}' 2>/dev/null | python3 -m json.tool 2>/dev/null || kubectl get patronicore -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o yaml 2>/dev/null | head -80 - echo "--- StatefulSet pod template securityContext ---" - kubectl get statefulset -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -l pgcluster=patroni -o jsonpath='{.items[0].spec.template.spec.securityContext}' 2>/dev/null | python3 -m json.tool 2>/dev/null || echo "(none or not found)" - kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o wide - kubectl get patronicores -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null || true - kubectl get statefulset,svc,pvc -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} 2>/dev/null || true - POD=$(kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -l name=patroni-core-operator -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true) - if [ -n "$POD" ]; then - echo "--- patroni-core-operator pod status ---" - kubectl get pod "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o wide - if [ "$(kubectl get pod "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -o jsonpath='{.status.phase}' 2>/dev/null)" != "Running" ]; then - echo "--- describe (patroni-core-operator not Running) ---" - kubectl describe pod "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} | tail -80 - echo "--- logs ---" - kubectl logs "$POD" -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --tail=100 2>/dev/null || true - fi - else - echo "No patroni-core-operator pod found." - fi - - echo "Waiting for operators and Patroni to be ready..." - kubectl wait --for=condition=ready pod -l name=postgres-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true - kubectl wait --for=condition=ready pod -l name=patroni-core-operator -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=300s || true - kubectl wait --for=condition=ready pod -l app=patroni -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} --timeout=600s || true - - echo "--- StatefulSet securityContext (after operator reconcile; must have fsGroup: 26) ---" - kubectl get statefulset -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} -l pgcluster=patroni -o jsonpath='{.items[0].spec.template.spec.securityContext}' 2>/dev/null | python3 -m json.tool 2>/dev/null || echo "(none or not found)" - echo "Pgskipper installation completed" - kubectl get pods -n ${{ vars.PGSKIPPER_INSTALL_NAMESPACE }} diff --git a/.github/workflows/upgrade_patroni_core_env_to_aws.yaml b/.github/workflows/upgrade_patroni_core_env_to_aws.yaml index 2c3de52d..66d17b54 100644 --- a/.github/workflows/upgrade_patroni_core_env_to_aws.yaml +++ b/.github/workflows/upgrade_patroni_core_env_to_aws.yaml @@ -1,5 +1,5 @@ -# Upgrade only the patroni-core Helm release to inject integrationTests.env into the CR. -# Use when the CR was installed without ATP/S3 env (e.g. before workflow fix) and you don't want a full reinstall. +# Upgrade only the patroni-core Helm release to inject ATP-related settings into the CR (spec.integrationTests). +# Use when the CR was installed without ATP/S3 values and you don't want a full reinstall. name: Upgrade PatroniCore env to AWS on: @@ -61,7 +61,7 @@ jobs: run: | aws eks update-kubeconfig --region ${{ vars.AWS_REGION || 'us-east-1' }} --name ${{ vars.AWS_CLUSTERNAME }} - - name: Upgrade patroni-core (inject integrationTests.env) + - name: Upgrade patroni-core (inject ATP Helm values) run: | NS="${{ vars.PGSKIPPER_INSTALL_NAMESPACE }}" GITHUB_GROUP=$(echo ${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]') @@ -84,7 +84,7 @@ jobs: [ -n "${{ vars.ATP_REPORT_VIEW_UI_URL }}" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.atpReportViewUiUrl=${{ vars.ATP_REPORT_VIEW_UI_URL }}" [ -n "$ENV_NAME" ] && TESTS_ATP_ARGS="$TESTS_ATP_ARGS --set tests.environmentName=$ENV_NAME" - echo "Upgrading pgskipper-patroni in $NS with TESTS_ATP_ARGS (env will be in CR)..." + echo "Upgrading pgskipper-patroni in $NS with TESTS_ATP_ARGS..." # -f values.yaml ensures chart defaults (tests.atpStorage, etc.) are in the merge; --reuse-values then overlay; --set overrides. helm upgrade --namespace="$NS" --install \ -f ./operator/charts/patroni-core/values.yaml \ @@ -92,7 +92,7 @@ jobs: $TESTS_ATP_ARGS \ pgskipper-patroni ./operator/charts/patroni-core - echo "--- PatroniCore spec.integrationTests (env should appear below) ---" + echo "--- PatroniCore spec.integrationTests ---" kubectl get patronicore -n "$NS" -o yaml | grep -A200 "integrationTests:" | head -80 echo "Delete the test pod so the operator recreates it with env: kubectl delete pod integration-robot-tests -n $NS" diff --git a/README.md b/README.md index f23b9a3c..a7be7f93 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,22 @@ Postgres-Operator provides PostgreSQL as a service on Kubernetes and OpenShift. Please refer to the [Quick Start Guide](/docs/public/quickstart.md) +### Integration tests and ATP storage + +Integration test settings live under `tests` in the Helm values for **patroni-core** and **patroni-services** (see [`operator/charts/patroni-core/values.yaml`](operator/charts/patroni-core/values.yaml) and [`operator/charts/patroni-services/values.yaml`](operator/charts/patroni-services/values.yaml)). The test image is based on [qubership-docker-integration-tests](https://github.com/Netcracker/qubership-docker-integration-tests). Optional `tests.atpStorage`, `tests.atpReportViewUiUrl`, and `tests.environmentName` map to the same `ATP_*` / `ENVIRONMENT_NAME` variables as in other Qubership demos (Consul `integrationTests.*`, RabbitMQ `tests.*`). The Patroni Services chart renders these into the custom resource (`operator/charts/patroni-services/templates/cr.yaml`). + +| Value (Helm) | Description | +|------------------------------|-------------| +| `tests.atpStorage.provider` | S3 provider (for example `aws`, `minio`, `s3`). When set, the chart can emit ATP storage environment variables for the test pod. | +| `tests.atpStorage.serverUrl` | S3 API endpoint URL. | +| `tests.atpStorage.serverUiUrl` | Optional storage UI URL. | +| `tests.atpStorage.bucket` | Bucket name; empty usually means no S3 upload in the base image flow. | +| `tests.atpStorage.region` | Region (for example for AWS). | +| `tests.atpStorage.username` | Access key (sensitive; prefer secrets / external overrides in real environments). | +| `tests.atpStorage.password` | Secret key (same as username). | +| `tests.atpReportViewUiUrl` | Optional Allure report UI base URL. | +| `tests.environmentName` | Optional logical name for paths or labels. | + ### Smoke tests There is no smoke tests. diff --git a/operator/api/apps/v1/postgresservice_types.go b/operator/api/apps/v1/postgresservice_types.go index 0ff26e22..e6695222 100644 --- a/operator/api/apps/v1/postgresservice_types.go +++ b/operator/api/apps/v1/postgresservice_types.go @@ -209,7 +209,6 @@ type IntegrationTests struct { PgNodeQty int `json:"pgNodeQty,omitempty"` PodLabels map[string]string `json:"podLabels,omitempty"` Affinity v1.Affinity `json:"affinity,omitempty"` - Env []v1.EnvVar `json:"env,omitempty"` } // ExternalDataBase defines the desired state of ExternalDataBase diff --git a/operator/api/apps/v1/zz_generated.deepcopy.go b/operator/api/apps/v1/zz_generated.deepcopy.go index 43963b5d..68b58c34 100644 --- a/operator/api/apps/v1/zz_generated.deepcopy.go +++ b/operator/api/apps/v1/zz_generated.deepcopy.go @@ -146,13 +146,6 @@ func (in *IntegrationTests) DeepCopyInto(out *IntegrationTests) { } } in.Affinity.DeepCopyInto(&out.Affinity) - if in.Env != nil { - in, out := &in.Env, &out.Env - *out = make([]corev1.EnvVar, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationTests. diff --git a/operator/api/common/v1/zz_generated.deepcopy.go b/operator/api/common/v1/zz_generated.deepcopy.go index a915d992..d6990f21 100644 --- a/operator/api/common/v1/zz_generated.deepcopy.go +++ b/operator/api/common/v1/zz_generated.deepcopy.go @@ -4,6 +4,8 @@ package v1 +import () + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Storage) DeepCopyInto(out *Storage) { *out = *in diff --git a/operator/api/patroni/v1/patronicore_types.go b/operator/api/patroni/v1/patronicore_types.go index cba5e314..1c672f6a 100644 --- a/operator/api/patroni/v1/patronicore_types.go +++ b/operator/api/patroni/v1/patronicore_types.go @@ -188,7 +188,6 @@ type IntegrationTests struct { PgNodeQty int `json:"pgNodeQty,omitempty"` PodLabels map[string]string `json:"podLabels,omitempty"` Affinity v1.Affinity `json:"affinity,omitempty"` - Env []v1.EnvVar `json:"env,omitempty"` } type Policies struct { diff --git a/operator/api/patroni/v1/zz_generated.deepcopy.go b/operator/api/patroni/v1/zz_generated.deepcopy.go index 419eaefd..a9ed7bd0 100644 --- a/operator/api/patroni/v1/zz_generated.deepcopy.go +++ b/operator/api/patroni/v1/zz_generated.deepcopy.go @@ -110,13 +110,6 @@ func (in *IntegrationTests) DeepCopyInto(out *IntegrationTests) { } } in.Affinity.DeepCopyInto(&out.Affinity) - if in.Env != nil { - in, out := &in.Env, &out.Env - *out = make([]corev1.EnvVar, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IntegrationTests. diff --git a/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml b/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml index 34b16ecd..4efaed11 100644 --- a/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml +++ b/operator/charts/patroni-core/crds/netcracker.com_patronicores.yaml @@ -1093,20 +1093,6 @@ spec: items: type: string type: array - env: - description: List of environment variables for the integration test pod (e.g. ATP_STORAGE_*) - items: - type: object - required: - - name - properties: - name: - type: string - value: - type: string - valueFrom: - type: object - type: array type: object ldap: properties: @@ -2650,9 +2636,10 @@ spec: operator: description: |- Operator represents a key's relationship to the value. - Valid operators are Exists and Equal. Defaults to Equal. + Valid operators are Exists, Equal, Lt, and Gt. Defaults to Equal. Exists is equivalent to wildcard for value, so that a pod can tolerate all taints of a particular category. + Lt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators). type: string tolerationSeconds: description: |- diff --git a/operator/charts/patroni-core/templates/cr.yaml b/operator/charts/patroni-core/templates/cr.yaml index 2312d0a1..ff363482 100644 --- a/operator/charts/patroni-core/templates/cr.yaml +++ b/operator/charts/patroni-core/templates/cr.yaml @@ -283,9 +283,6 @@ spec: - name: ATP_REPORT_VIEW_UI_URL value: {{ .Values.tests.atpReportViewUiUrl | quote }} {{- end }} - {{- if .Values.tests.env }} - {{- toYaml .Values.tests.env | nindent 4 }} - {{- end }} {{ end }} {{ if .Values.runTestsOnly }} runTestsTime: {{ now | unixEpoch | quote }} diff --git a/operator/charts/patroni-core/values.yaml b/operator/charts/patroni-core/values.yaml index c7db8376..40ec5b71 100644 --- a/operator/charts/patroni-core/values.yaml +++ b/operator/charts/patroni-core/values.yaml @@ -177,7 +177,7 @@ patroni: patroniParams: - "failsafe_mode: true" - "primary_start_timeout: 30" - - "retry_timeout: 10" + - "retry_timeout: 600" # Storage section. storage: @@ -286,7 +286,6 @@ tests: password: "" atpReportViewUiUrl: "https://test.com" environmentName: "pgskipper-operator" - env: [] # One of "full", "basic"or one from testScenarios runTestScenarios: "basic" testScenarios: diff --git a/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml b/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml index 8400afe2..46518478 100644 --- a/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml +++ b/operator/charts/patroni-services/crds/netcracker.com_patroniservices.yaml @@ -3553,163 +3553,6 @@ spec: x-kubernetes-list-type: atomic type: object type: object - env: - items: - description: EnvVar represents an environment variable present - in a Container. - properties: - name: - description: |- - Name of the environment variable. - May consist of any printable ASCII characters except '='. - type: string - value: - description: |- - Variable references $(VAR_NAME) are expanded - using the previously defined environment variables in the container and - any service environment variables. If a variable cannot be resolved, - the reference in the input string will be unchanged. Double $$ are reduced - to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. - "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". - Escaped references will never be expanded, regardless of whether the variable - exists or not. - Defaults to "". - type: string - valueFrom: - description: Source for the environment variable's value. - Cannot be used if value is not empty. - properties: - configMapKeyRef: - description: Selects a key of a ConfigMap. - properties: - key: - description: The key to select. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the ConfigMap or its - key must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - fieldRef: - description: |- - Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, - spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. - properties: - apiVersion: - description: Version of the schema the FieldPath - is written in terms of, defaults to "v1". - type: string - fieldPath: - description: Path of the field to select in the - specified API version. - type: string - required: - - fieldPath - type: object - x-kubernetes-map-type: atomic - fileKeyRef: - description: |- - FileKeyRef selects a key of the env file. - Requires the EnvFiles feature gate to be enabled. - properties: - key: - description: |- - The key within the env file. An invalid key will prevent the pod from starting. - The keys defined within a source may consist of any printable ASCII characters except '='. - During Alpha stage of the EnvFiles feature gate, the key size is limited to 128 characters. - type: string - optional: - default: false - description: |- - Specify whether the file or its key must be defined. If the file or key - does not exist, then the env var is not published. - If optional is set to true and the specified key does not exist, - the environment variable will not be set in the Pod's containers. - - If optional is set to false and the specified key does not exist, - an error will be returned during Pod creation. - type: boolean - path: - description: |- - The path within the volume from which to select the file. - Must be relative and may not contain the '..' path or start with '..'. - type: string - volumeName: - description: The name of the volume mount containing - the env file. - type: string - required: - - key - - path - - volumeName - type: object - x-kubernetes-map-type: atomic - resourceFieldRef: - description: |- - Selects a resource of the container: only resources limits and requests - (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. - properties: - containerName: - description: 'Container name: required for volumes, - optional for env vars' - type: string - divisor: - anyOf: - - type: integer - - type: string - description: Specifies the output format of the - exposed resources, defaults to "1" - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - resource: - description: 'Required: resource to select' - type: string - required: - - resource - type: object - x-kubernetes-map-type: atomic - secretKeyRef: - description: Selects a key of a secret in the pod's - namespace - properties: - key: - description: The key of the secret to select from. Must - be a valid secret key. - type: string - name: - default: "" - description: |- - Name of the referent. - This field is effectively required, but due to backwards compatibility is - allowed to be empty. Instances of this type with an empty value here are - almost certainly wrong. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names - type: string - optional: - description: Specify whether the Secret or its key - must be defined - type: boolean - required: - - key - type: object - x-kubernetes-map-type: atomic - type: object - required: - - name - type: object - type: array image: type: string pgNodeQty: diff --git a/operator/charts/patroni-services/templates/cr.yaml b/operator/charts/patroni-services/templates/cr.yaml index e6bc8386..bb69c345 100644 --- a/operator/charts/patroni-services/templates/cr.yaml +++ b/operator/charts/patroni-services/templates/cr.yaml @@ -117,7 +117,7 @@ spec: secretAccessKey: {{ .Values.backupDaemon.s3Storage.secretAccessKey }} bucket: {{ .Values.backupDaemon.s3Storage.bucket }} prefix: {{ default "postgres" .Values.backupDaemon.s3Storage.prefix }} - untrustedCert: {{ default "true" .Values.backupDaemon.s3Storage.untrustedCert }} + untrustedCert: {{ .Values.backupDaemon.s3Storage.untrustedCert }} region: {{ .Values.backupDaemon.s3Storage.region }} {{ end }} {{ if .Values.backupDaemon.externalPv }} @@ -423,7 +423,7 @@ spec: {{- end }} {{ end }} pgNodeQty: {{ default "1" .Values.patroni.replicas }} - {{- if or .Values.tests.environmentName (and .Values.tests.atpStorage .Values.tests.atpStorage.provider) (and .Values.tests.env (len .Values.tests.env | gt 0)) }} + {{- if or .Values.tests.environmentName (and .Values.tests.atpStorage .Values.tests.atpStorage.provider) }} env: {{- if .Values.tests.environmentName }} - name: ENVIRONMENT_NAME @@ -461,9 +461,6 @@ spec: - name: ATP_REPORT_VIEW_UI_URL value: {{ .Values.tests.atpReportViewUiUrl | quote }} {{- end }} - {{- if .Values.tests.env }} - {{- toYaml .Values.tests.env | nindent 4 }} - {{- end }} {{- end }} {{ end }} {{ if .Values.runTestsOnly }} diff --git a/operator/charts/patroni-services/values.yaml b/operator/charts/patroni-services/values.yaml index a770a20d..062a49bb 100644 --- a/operator/charts/patroni-services/values.yaml +++ b/operator/charts/patroni-services/values.yaml @@ -444,8 +444,6 @@ tests: password: "" atpReportViewUiUrl: "" environmentName: "" - # Extra env vars for integration test pod (optional) - env: [] # One of "full", "basic"or one from testScenarios runTestScenarios: "basic" testScenarios: diff --git a/operator/controllers/patroni_core_controller.go b/operator/controllers/patroni_core_controller.go index 137af266..fd141d76 100644 --- a/operator/controllers/patroni_core_controller.go +++ b/operator/controllers/patroni_core_controller.go @@ -460,9 +460,6 @@ func (pr *PatroniCoreReconciler) AddExcludeLabelToCm(c client.Client, cmName str func (pr *PatroniCoreReconciler) createTestsPods(cr *qubershipv1.PatroniCore) error { if cr.Spec.IntegrationTests != nil { - if n := len(cr.Spec.IntegrationTests.Env); n > 0 { - pr.logger.Info("CR has integrationTests.env, passing to test pod", zap.Int("envCount", n)) - } integrationTestsPod := deployment.NewCoreIntegrationTests(cr, utils.GetPatroniClusterSettings(cr.Spec.Patroni.ClusterName)) state, err := utils.GetPodPhase(integrationTestsPod) if err != nil { diff --git a/operator/pkg/deployment/tests.go b/operator/pkg/deployment/tests.go index ae4a5ccc..3b6598a3 100644 --- a/operator/pkg/deployment/tests.go +++ b/operator/pkg/deployment/tests.go @@ -133,9 +133,6 @@ func NewIntegrationTestsPod(cr *v1.PatroniServices, cluster *patroniv1.PatroniCl RestartPolicy: corev1.RestartPolicyNever, }, } - if len(testsSpec.Env) > 0 { - pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, testsSpec.Env...) - } if testsSpec.Resources != nil { pod.Spec.Containers[0].Resources = *testsSpec.Resources } @@ -256,9 +253,6 @@ func NewCoreIntegrationTests(cr *patroniv1.PatroniCore, cluster *patroniv1.Patro RestartPolicy: corev1.RestartPolicyNever, }, } - if len(testsSpec.Env) > 0 { - pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, testsSpec.Env...) - } if testsSpec.Resources != nil { pod.Spec.Containers[0].Resources = *testsSpec.Resources } diff --git a/tests/docker/requirements.txt b/tests/docker/requirements.txt index ac76eb16..e7fd341e 100644 --- a/tests/docker/requirements.txt +++ b/tests/docker/requirements.txt @@ -41,4 +41,4 @@ typing_extensions==4.2.0 urllib3~=2.3.0 websocket-client==1.9.0 Werkzeug==3.1.5 -zipp==3.23.0 \ No newline at end of file +zipp==3.23.0