Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .github/workflows/build-push-image-private.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Build and push Docker image to ECR (private)

on:
push:
branches:
- release/v*
- feature/*

permissions:
contents: read

env:
AWS_REGION: eu-north-1
ECR_REPOSITORY: 288509344804.dkr.ecr.eu-north-1.amazonaws.com
IMAGE_NAME: core.postgres-operator

jobs:
ci:
name: Build and push Docker image to ECR
runs-on: ubuntu-22.04

steps:
- name: Checkout main repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "^1.26.4"
cache-dependency-path: go.sum

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.CORE_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.CORE_AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Parse release version from Git commit message
run: |
readonly VERSION_REGEX="v[0-9]+\.[0-9]+\.[0-9]+"

VERSION_PREV="$(git for-each-ref --sort=creatordate --format "%(refname:lstrip=2)" refs/tags | grep -E "^${VERSION_REGEX}$" | tail -1)"

if [[ -z "${VERSION_PREV}" ]]; then
>&2 echo "At least one version tag is required in the repository."
>&2 echo "Tag a Git commit on the main branch manually before running the workflow."
exit 1
fi

readonly GIT_BRANCH="${GITHUB_REF#refs/heads/}"

GIT_BRANCH_SANITIZED="${GIT_BRANCH}"
GIT_BRANCH_SANITIZED="${GIT_BRANCH_SANITIZED//\//-}"
GIT_BRANCH_SANITIZED="${GIT_BRANCH_SANITIZED//_/-}"
VERSION="${VERSION_PREV}-${GITHUB_SHA:0:7}-${GIT_BRANCH_SANITIZED}"
VERSION="${VERSION:0:128}"
VERSION="${VERSION%-}"

echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"

- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: private

- name: Run unit tests
run: |
make mocks
make linux
go test -race ./...

- name: Build and push Docker image
run: |
readonly IMAGE="${ECR_REPOSITORY}/${IMAGE_NAME}"
readonly TAG="${VERSION}"

docker build --platform linux/amd64 \
-f docker/Dockerfile \
--build-arg VERSION="${VERSION}" \
-t "${IMAGE}:${TAG}" \
.

docker push "${IMAGE}:${TAG}"
99 changes: 99 additions & 0 deletions .github/workflows/build-push-image-public.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Build and push Docker image to ECR (public)

on:
push:
branches:
- master

permissions:
contents: write

env:
AWS_REGION: us-east-1
ECR_REPOSITORY: public.ecr.aws/edenlabllc
GIT_USERNAME: github-actions
GIT_EMAIL: github-actions@github.com
IMAGE_NAME: core.postgres-operator

jobs:
ci:
name: Build and push Docker image to ECR
runs-on: ubuntu-22.04

steps:
- name: Checkout main repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "^1.26.4"
cache-dependency-path: go.sum

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.CORE_AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.CORE_AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Parse release version from Git commit history
run: |
echo "Git commit message:"
GIT_COMMIT_MSG="$(git log -1 --pretty=format:"%s")"
echo "${GIT_COMMIT_MSG}"

VERSION_REGEX="^Merge pull request #[0-9]+ from ${GITHUB_REPOSITORY_OWNER}/release/(v[0-9]+\.[0-9]+\.[0-9]+)$"
if [[ ! "${GIT_COMMIT_MSG}" =~ ${VERSION_REGEX} ]]; then
>&2 echo "Pushes to master should be done via merges of PR requests from release branches only."
>&2 echo " release branch - release/vN.N.N"
>&2 echo "The expected message format (will be used for parsing a release tag):"
>&2 echo "Merge pull request #N from ${GITHUB_REPOSITORY_OWNER}/release/vN.N.N"
exit 1
fi

VERSION="${BASH_REMATCH[1]}"
echo "VERSION=${VERSION}" >> "${GITHUB_ENV}"

- name: Login to ECR
uses: aws-actions/amazon-ecr-login@v2
with:
registry-type: public

- name: Run unit tests
run: |
make mocks
make linux
go test -race ./...

- name: Build and push Docker image
run: |
readonly IMAGE="${ECR_REPOSITORY}/${IMAGE_NAME}"
readonly TAG="${VERSION}"

docker build --platform linux/amd64 \
-f docker/Dockerfile \
--build-arg VERSION="${VERSION}" \
-t "${IMAGE}:${TAG}" \
.

docker push "${IMAGE}:${TAG}"

- name: Create Git tag and GitHub release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git config user.name "${GIT_USERNAME}"
git config user.email "${GIT_EMAIL}"

if git rev-parse "${VERSION}" >/dev/null 2>&1; then
echo "Git tag ${VERSION} already exists"
else
git tag -m "${VERSION}" "${VERSION}"
git push origin "${VERSION}"
fi

gh release view "${VERSION}" >/dev/null 2>&1 \
|| gh release create "${VERSION}" --title "${VERSION}" --generate-notes
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,34 @@ clusters on Kubernetes (K8s) powered by [Patroni](https://github.com/zalando/pat
It is configured only through Postgres manifests (CRDs) to ease integration into automated CI/CD
pipelines with no access to Kubernetes API directly, promoting infrastructure as code vs manual operations.

## Changes by Edenlab LLC

This fork by [Edenlab LLC](https://edenlab.io/) extends the `connectionPooler` section of the
Postgres CR with optional, backward-compatible pooler-specific pod template and deployment
settings. When omitted, behavior matches upstream Zalando Postgres Operator v1.15.1.

New fields:

* **inheritPodAnnotations** / **podAnnotations** — apply `spec.podAnnotations` to Spilo pods only
and set pooler annotations separately (e.g. keep Prometheus scrape on Postgres, not on PgBouncer).
* **inheritPodLabels** / **podLabels** — same pattern for labels.
* **nodeAffinity** / **podAntiAffinity** — pooler-specific scheduling overrides.
* **deploymentStrategy** — control pooler Deployment rollout (e.g. `maxSurge: 0` /
`maxUnavailable: 1` on small clusters with required pod anti-affinity).

See [connection pooler parameters](docs/reference/cluster_manifest.md#connection-pooler) and
[examples](docs/user.md) for details.

It also adds GitHub Actions [workflows](.github/workflows) to build and publish Docker images.

Images are published to the public ECR gallery:
[public.ecr.aws/edenlabllc/core.postgres-operator](https://gallery.ecr.aws/edenlabllc/core.postgres-operator)

Fork changes are based on upstream
[v1.15.1](https://github.com/zalando/postgres-operator/releases/tag/v1.15.1) and released under
Git tags such as [`v1.15.2`](https://github.com/edenlabllc/postgres-operator/releases/tag/v1.15.2)
(Gitflow: merge `release/v1.15.2` into `master`, CI tags and publishes the image).

### Operator features

* Rolling updates on Postgres cluster changes, incl. quick minor version updates
Expand Down
36 changes: 36 additions & 0 deletions charts/postgres-operator/crds/postgresqls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,42 @@ spec:
type: string
user:
type: string
inheritPodAnnotations:
type: boolean
podAnnotations:
additionalProperties:
type: string
type: object
inheritPodLabels:
type: boolean
podLabels:
additionalProperties:
type: string
type: object
nodeAffinity:
type: object
x-kubernetes-preserve-unknown-fields: true
podAntiAffinity:
properties:
enabled:
type: boolean
preferredDuringScheduling:
type: boolean
topologyKey:
type: string
type: object
deploymentStrategy:
properties:
type:
type: string
rollingUpdate:
properties:
maxSurge:
x-kubernetes-int-or-string: true
maxUnavailable:
x-kubernetes-int-or-string: true
type: object
type: object
type: object
databases:
additionalProperties:
Expand Down
41 changes: 41 additions & 0 deletions docs/reference/cluster_manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,47 @@ for both master and replica pooler services (if `enableReplicaConnectionPooler`
* **resources**
Resource configuration for connection pooler deployment.

* **inheritPodAnnotations**
When `true` (default), `spec.podAnnotations` and operator `custom_pod_annotations`
are applied to connection pooler pods as well as Spilo pods. Set to `false` to
apply annotations to Spilo pods only and use `connectionPooler.podAnnotations`
for the pooler.

* **podAnnotations**
Annotations applied to connection pooler pods only. Merged on top of inherited
annotations when `inheritPodAnnotations` is `true`.

* **inheritPodLabels**
When `true` (default), labels from the Postgres CR metadata listed under operator
`inherited_labels` are applied to connection pooler pods. Set to `false` to
skip them.

* **podLabels**
Extra labels applied to connection pooler pods only, merged on top of the base
pooler labels.

* **nodeAffinity**
Node affinity for connection pooler pods. When omitted, `spec.nodeAffinity` is
used.

* **podAntiAffinity**
Pod anti-affinity for connection pooler pods. When omitted, operator-level
`enable_pod_antiaffinity`, `pod_antiaffinity_preferred_during_scheduling`, and
`pod_antiaffinity_topology_key` apply.

* **enabled** - enable or disable pod anti-affinity for the pooler.
* **preferredDuringScheduling** - use soft (`true`) or hard (`false`) anti-affinity.
* **topologyKey** - topology key for anti-affinity (default from operator config).

* **deploymentStrategy**
Rollout strategy for the pooler Deployment. When omitted, the operator does
not set strategy and Kubernetes defaults apply (`RollingUpdate` with 25%
`maxSurge` / `maxUnavailable`).

* **type** - deployment strategy type, typically `RollingUpdate`.
* **rollingUpdate.maxSurge** - maximum extra pods during rollout (e.g. `0`).
* **rollingUpdate.maxUnavailable** - maximum unavailable pods during rollout (e.g. `1`).

## Custom TLS certificates

Those parameters are grouped under the `tls` top-level key. Note, you have to
Expand Down
21 changes: 21 additions & 0 deletions docs/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,11 @@ each Postgres cluster, specify:

```
spec:
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8008"
prometheus.io/path: /metrics

connectionPooler:
# how many instances of connection pooler to create
numberOfInstances: 2
Expand All @@ -1212,6 +1217,22 @@ spec:
limits:
cpu: "1"
memory: 100Mi

# keep spec.podAnnotations on Spilo pods only; pooler gets podAnnotations below
inheritPodAnnotations: false
podAnnotations:
linkerd.io/inject: enabled

# optional pooler-specific scheduling (omit to use spec/operator defaults)
podAntiAffinity:
enabled: true
preferredDuringScheduling: false
topologyKey: kubernetes.io/hostname
deploymentStrategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 0
maxUnavailable: 1
```

The `enableConnectionPooler` flag is not required when the `connectionPooler`
Expand Down
33 changes: 32 additions & 1 deletion manifests/complete-postgres-manifest.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,11 @@ spec:
# spiloRunAsGroup: 103
# spiloFSGroup: 103
# podAnnotations:
# annotation.key: value
# prometheus.io/scrape: "true"
# prometheus.io/port: "8008"
# prometheus.io/path: /metrics
# # podAnnotations apply to Spilo pods and, by default, to connection pooler pods too.
# # Use connectionPooler.inheritPodAnnotations: false to keep them on DB pods only.
# serviceAnnotations:
# annotation.key: value
# podPriorityClassName: "spilo-pod-priority"
Expand Down Expand Up @@ -184,6 +188,33 @@ spec:
# limits:
# cpu: "1"
# memory: 100Mi
# # Pooler-specific pod annotations (optional). By default pooler inherits spec.podAnnotations.
# inheritPodAnnotations: false
# podAnnotations:
# linkerd.io/inject: enabled
# # Pooler-specific pod labels (optional). By default pooler inherits inherited_labels from CR metadata.
# inheritPodLabels: true
# podLabels: {}
# # Override spec.nodeAffinity for pooler pods only (optional).
# nodeAffinity:
# requiredDuringSchedulingIgnoredDuringExecution:
# nodeSelectorTerms:
# - matchExpressions:
# - key: db
# operator: In
# values:
# - postgres
# # Override operator-level pod anti-affinity for pooler pods (optional).
# podAntiAffinity:
# enabled: true
# preferredDuringScheduling: false
# topologyKey: kubernetes.io/hostname
# # Deployment rollout strategy for pooler (optional). When omitted, Kubernetes defaults apply.
# deploymentStrategy:
# type: RollingUpdate
# rollingUpdate:
# maxSurge: 0
# maxUnavailable: 1

initContainers:
- name: date
Expand Down
Loading
Loading