├── .flake8 ├── .github ├── ISSUE_TEMPLATE │ └── postgres-operator-issue-template.md ├── PULL_REQUEST_TEMPLATE │ └── postgres-operator-pull-request-template.md └── workflows │ ├── publish_ghcr_image.yaml │ ├── run_e2e.yaml │ └── run_tests.yaml ├── .gitignore ├── .golangci.yml ├── .zappr.yaml ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS ├── Makefile ├── README.md ├── SECURITY.md ├── build-ci.sh ├── charts ├── postgres-operator-ui │ ├── .helmignore │ ├── Chart.yaml │ ├── index.yaml │ ├── postgres-operator-ui-1.10.1.tgz │ ├── postgres-operator-ui-1.11.0.tgz │ ├── postgres-operator-ui-1.12.2.tgz │ ├── postgres-operator-ui-1.13.0.tgz │ ├── postgres-operator-ui-1.14.0.tgz │ ├── postgres-operator-ui-1.9.0.tgz │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── clusterrole.yaml │ │ ├── clusterrolebinding.yaml │ │ ├── deployment.yaml │ │ ├── ingress.yaml │ │ ├── service.yaml │ │ └── serviceaccount.yaml │ └── values.yaml └── postgres-operator │ ├── .helmignore │ ├── Chart.yaml │ ├── crds │ ├── operatorconfigurations.yaml │ ├── postgresqls.yaml │ └── postgresteams.yaml │ ├── index.yaml │ ├── postgres-operator-1.10.1.tgz │ ├── postgres-operator-1.11.0.tgz │ ├── postgres-operator-1.12.2.tgz │ ├── postgres-operator-1.13.0.tgz │ ├── postgres-operator-1.14.0.tgz │ ├── postgres-operator-1.9.0.tgz │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── clusterrole-postgres-pod.yaml │ ├── clusterrole.yaml │ ├── clusterrolebinding.yaml │ ├── configmap.yaml │ ├── deployment.yaml │ ├── operatorconfiguration.yaml │ ├── postgres-pod-priority-class.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ └── user-facing-clusterroles.yaml │ └── values.yaml ├── cmd └── main.go ├── delivery.yaml ├── docker ├── DebugDockerfile ├── Dockerfile └── build_operator.sh ├── docs ├── administrator.md ├── developer.md ├── diagrams │ ├── Makefile │ ├── logo.png │ ├── neutral_operator.excalidraw │ ├── neutral_operator.png │ ├── neutral_operator_dark.png │ ├── neutral_operator_light.png │ ├── operator.png │ ├── operator.tex │ ├── pgui-cluster-list.png │ ├── pgui-cluster-startup.png │ ├── pgui-delete-cluster.png │ ├── pgui-finished-setup.png │ ├── pgui-new-cluster.png │ ├── pgui-operator-logs.png │ ├── pgui-waiting-for-master.png │ ├── pod.png │ └── pod.tex ├── index.md ├── operator-ui.md ├── quickstart.md ├── reference │ ├── cluster_manifest.md │ ├── command_line_and_environment.md │ └── operator_parameters.md └── user.md ├── e2e ├── Dockerfile ├── Makefile ├── README.md ├── exec.sh ├── exec_into_env.sh ├── kind-cluster-postgres-operator-e2e-tests.yaml ├── requirements.txt ├── run.sh ├── scripts │ ├── cleanup.sh │ ├── get_logs.sh │ └── watch_objects.sh └── tests │ ├── __init__.py │ ├── k8s_api.py │ └── test_e2e.py ├── go.mod ├── go.sum ├── hack ├── custom-boilerplate.go.txt ├── tools.go ├── update-codegen.sh └── verify-codegen.sh ├── kubectl-pg ├── README.md ├── build.sh ├── cmd │ ├── addDb.go │ ├── addUser.go │ ├── check.go │ ├── connect.go │ ├── create.go │ ├── delete.go │ ├── extVolume.go │ ├── list.go │ ├── logs.go │ ├── root.go │ ├── scale.go │ ├── update.go │ ├── util.go │ └── version.go ├── go.mod ├── go.sum └── main.go ├── logical-backup ├── Dockerfile └── dump.sh ├── manifests ├── api-service.yaml ├── complete-postgres-manifest.yaml ├── configmap.yaml ├── custom-team-membership.yaml ├── e2e-storage-class.yaml ├── fake-teams-api.yaml ├── fes.crd.yaml ├── infrastructure-roles-configmap.yaml ├── infrastructure-roles-new.yaml ├── infrastructure-roles.yaml ├── kustomization.yaml ├── minimal-fake-pooler-deployment.yaml ├── minimal-master-replica-svcmonitor.yaml ├── minimal-postgres-lowest-version-manifest.yaml ├── minimal-postgres-manifest.yaml ├── operator-service-account-rbac-openshift.yaml ├── operator-service-account-rbac.yaml ├── operatorconfiguration.crd.yaml ├── platform-credentials.yaml ├── postgres-operator.yaml ├── postgres-pod-priority-class.yaml ├── postgresql-operator-default-configuration.yaml ├── postgresql.crd.yaml ├── postgresteam.crd.yaml ├── standby-manifest.yaml └── user-facing-clusterroles.yaml ├── mkdocs.yml ├── mocks └── mocks.go ├── pkg ├── apis │ ├── acid.zalan.do │ │ ├── register.go │ │ └── v1 │ │ │ ├── const.go │ │ │ ├── crds.go │ │ │ ├── doc.go │ │ │ ├── marshal.go │ │ │ ├── operator_configuration_type.go │ │ │ ├── postgres_team_type.go │ │ │ ├── postgresql_type.go │ │ │ ├── register.go │ │ │ ├── util.go │ │ │ ├── util_test.go │ │ │ └── zz_generated.deepcopy.go │ └── zalando.org │ │ ├── register.go │ │ └── v1 │ │ ├── fabriceventstream.go │ │ ├── register.go │ │ └── zz_generated.deepcopy.go ├── apiserver │ ├── apiserver.go │ └── apiserver_test.go ├── cluster │ ├── cluster.go │ ├── cluster_test.go │ ├── connection_pooler.go │ ├── connection_pooler_new_test.go │ ├── connection_pooler_test.go │ ├── database.go │ ├── exec.go │ ├── filesystems.go │ ├── k8sres.go │ ├── k8sres_test.go │ ├── majorversionupgrade.go │ ├── pod.go │ ├── pod_test.go │ ├── resources.go │ ├── streams.go │ ├── streams_test.go │ ├── sync.go │ ├── sync_test.go │ ├── types.go │ ├── util.go │ ├── util_test.go │ ├── volumes.go │ └── volumes_test.go ├── controller │ ├── controller.go │ ├── logs_and_api.go │ ├── node.go │ ├── node_test.go │ ├── operator_config.go │ ├── pod.go │ ├── postgresql.go │ ├── postgresql_test.go │ ├── types.go │ ├── util.go │ └── util_test.go ├── generated │ ├── clientset │ │ └── versioned │ │ │ ├── clientset.go │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ └── typed │ │ │ ├── acid.zalan.do │ │ │ └── v1 │ │ │ │ ├── acid.zalan.do_client.go │ │ │ │ ├── doc.go │ │ │ │ ├── fake │ │ │ │ ├── doc.go │ │ │ │ ├── fake_acid.zalan.do_client.go │ │ │ │ ├── fake_operatorconfiguration.go │ │ │ │ ├── fake_postgresql.go │ │ │ │ └── fake_postgresteam.go │ │ │ │ ├── generated_expansion.go │ │ │ │ ├── operatorconfiguration.go │ │ │ │ ├── postgresql.go │ │ │ │ └── postgresteam.go │ │ │ └── zalando.org │ │ │ └── v1 │ │ │ ├── doc.go │ │ │ ├── fabriceventstream.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_fabriceventstream.go │ │ │ └── fake_zalando.org_client.go │ │ │ ├── generated_expansion.go │ │ │ └── zalando.org_client.go │ ├── informers │ │ └── externalversions │ │ │ ├── acid.zalan.do │ │ │ ├── interface.go │ │ │ └── v1 │ │ │ │ ├── interface.go │ │ │ │ ├── postgresql.go │ │ │ │ └── postgresteam.go │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ ├── internalinterfaces │ │ │ └── factory_interfaces.go │ │ │ └── zalando.org │ │ │ ├── interface.go │ │ │ └── v1 │ │ │ ├── fabriceventstream.go │ │ │ └── interface.go │ └── listers │ │ ├── acid.zalan.do │ │ └── v1 │ │ │ ├── expansion_generated.go │ │ │ ├── postgresql.go │ │ │ └── postgresteam.go │ │ └── zalando.org │ │ └── v1 │ │ ├── expansion_generated.go │ │ └── fabriceventstream.go ├── spec │ ├── types.go │ └── types_test.go ├── teams │ ├── postgres_team.go │ └── postgres_team_test.go └── util │ ├── config │ ├── config.go │ ├── config_test.go │ └── util.go │ ├── constants │ ├── annotations.go │ ├── aws.go │ ├── kubernetes.go │ ├── pooler.go │ ├── postgresql.go │ ├── roles.go │ ├── streams.go │ └── units.go │ ├── filesystems │ ├── ext234.go │ └── filesystems.go │ ├── httpclient │ └── httpclient.go │ ├── k8sutil │ └── k8sutil.go │ ├── nicediff │ └── diff.go │ ├── patroni │ ├── patroni.go │ └── patroni_test.go │ ├── retryutil │ ├── retry_util.go │ └── retry_util_test.go │ ├── ringlog │ └── ringlog.go │ ├── teams │ ├── teams.go │ └── teams_test.go │ ├── users │ └── users.go │ ├── util.go │ ├── util_test.go │ └── volumes │ ├── ebs.go │ ├── ebs_test.go │ ├── volumes.go │ └── volumes_test.go ├── run_operator_locally.sh └── ui ├── .dockerignore ├── Dockerfile ├── MANIFEST.in ├── Makefile ├── app ├── .eslintignore ├── .eslintrc.yml ├── README.rst ├── package.json ├── src │ ├── app.js │ ├── app.tag.pug │ ├── edit.tag.pug │ ├── help-edit.tag.pug │ ├── help-general.tag.pug │ ├── logs.tag.pug │ ├── new.tag.pug │ ├── postgresql.tag.pug │ ├── postgresqls.tag.pug │ ├── prism.js │ ├── restore.tag.pug │ └── status.tag.pug └── webpack.config.js ├── manifests ├── deployment.yaml ├── ingress.yaml ├── kustomization.yaml ├── service.yaml └── ui-service-account-rbac.yaml ├── operator_ui ├── __init__.py ├── __main__.py ├── adapters │ ├── __init__.py │ └── logger.py ├── backoff.py ├── cluster_discovery.py ├── main.py ├── mock.py ├── spiloutils.py ├── static │ ├── favicon-96x96.png │ ├── prism.css │ ├── prism.js │ └── styles.css ├── templates │ └── index.html ├── update.py └── utils.py ├── requirements.txt ├── run_local.sh ├── setup.py ├── start_server.sh └── tox.ini /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude=.git,__pycache__ 3 | max-line-length=120 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Postgres Operator issue template 3 | about: How are you using the operator? 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | Please, answer some short questions which should help us to understand your problem / question better? 11 | 12 | - **Which image of the operator are you using?** e.g. ghcr.io/zalando/postgres-operator:v1.13.0 13 | - **Where do you run it - cloud or metal? Kubernetes or OpenShift?** [AWS K8s | GCP ... | Bare Metal K8s] 14 | - **Are you running Postgres Operator in production?** [yes | no] 15 | - **Type of issue?** [Bug report, question, feature request, etc.] 16 | 17 | Some general remarks when posting a bug report: 18 | - Please, check the operator, pod (Patroni) and postgresql logs first. When copy-pasting many log lines please do it in a separate GitHub gist together with your Postgres CRD and configuration manifest. 19 | - If you feel this issue might be more related to the [Spilo](https://github.com/zalando/spilo/issues) docker image or [Patroni](https://github.com/zalando/patroni/issues), consider opening issues in the respective repos. 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/postgres-operator-pull-request-template.md: -------------------------------------------------------------------------------- 1 | ## Problem description 2 | 3 | 4 | 5 | ## Linked issues 6 | 7 | 8 | 9 | ## Checklist 10 | 11 | Thanks for submitting a pull request to the Postgres Operator project. 12 | Please, ensure your contribution matches the following items: 13 | 14 | - [ ] Your go code is [formatted](https://blog.golang.org/gofmt). Your IDE should do it automatically for you. 15 | - [ ] You have updated [generated code](https://github.com/zalando/postgres-operator/blob/master/docs/developer.md#code-generation) when introducing new fields to the `acid.zalan.do` api package. 16 | - [ ] New [configuration options](https://github.com/zalando/postgres-operator/blob/master/docs/developer.md#introduce-additional-configuration-parameters) are reflected in CRD validation, helm charts and sample manifests. 17 | - [ ] New functionality is covered by [unit](https://github.com/zalando/postgres-operator/blob/master/docs/developer.md#unit-tests) and/or [e2e](https://github.com/zalando/postgres-operator/blob/master/docs/developer.md#end-to-end-tests) tests. 18 | - [ ] You have checked existing open PRs for possible overlay and referenced them. 19 | -------------------------------------------------------------------------------- /.github/workflows/publish_ghcr_image.yaml: -------------------------------------------------------------------------------- 1 | name: Publish multiarch postgres-operator images on ghcr.io 2 | 3 | env: 4 | REGISTRY: ghcr.io 5 | IMAGE_NAME: ${{ github.repository }} 6 | IMAGE_NAME_UI: ${{ github.repository }}-ui 7 | 8 | on: 9 | push: 10 | tags: 11 | - '*' 12 | 13 | jobs: 14 | publish: 15 | name: Build, test and push image 16 | runs-on: ubuntu-latest 17 | permissions: 18 | contents: read 19 | packages: write 20 | steps: 21 | - name: Checkout repository 22 | uses: actions/checkout@v3 23 | 24 | - uses: actions/setup-go@v2 25 | with: 26 | go-version: "^1.23.4" 27 | 28 | - name: Run unit tests 29 | run: make deps mocks test 30 | 31 | - name: Define image name 32 | id: image 33 | run: | 34 | OPERATOR_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${GITHUB_REF/refs\/tags\//}" 35 | echo "OPERATOR_IMAGE=$OPERATOR_IMAGE" >> $GITHUB_OUTPUT 36 | 37 | - name: Define UI image name 38 | id: image_ui 39 | run: | 40 | UI_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME_UI }}:${GITHUB_REF/refs\/tags\//}" 41 | echo "UI_IMAGE=$UI_IMAGE" >> $GITHUB_OUTPUT 42 | 43 | - name: Define logical backup image name 44 | id: image_lb 45 | run: | 46 | BACKUP_IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/logical-backup:${GITHUB_REF_NAME}" 47 | echo "BACKUP_IMAGE=$BACKUP_IMAGE" >> $GITHUB_OUTPUT 48 | 49 | - name: Set up QEMU 50 | uses: docker/setup-qemu-action@v2 51 | 52 | - name: Set up Docker Buildx 53 | uses: docker/setup-buildx-action@v2 54 | 55 | - name: Login to GHCR 56 | uses: docker/login-action@v2 57 | with: 58 | registry: ${{ env.REGISTRY }} 59 | username: ${{ github.actor }} 60 | password: ${{ secrets.GITHUB_TOKEN }} 61 | 62 | - name: Build and push multiarch operator image to ghcr 63 | uses: docker/build-push-action@v3 64 | with: 65 | context: . 66 | file: docker/Dockerfile 67 | push: true 68 | build-args: BASE_IMAGE=alpine:3 69 | tags: "${{ steps.image.outputs.OPERATOR_IMAGE }}" 70 | platforms: linux/amd64,linux/arm64 71 | 72 | - name: Build and push multiarch ui image to ghcr 73 | uses: docker/build-push-action@v3 74 | with: 75 | context: ui 76 | push: true 77 | build-args: BASE_IMAGE=python:3.11-slim 78 | tags: "${{ steps.image_ui.outputs.UI_IMAGE }}" 79 | platforms: linux/amd64,linux/arm64 80 | 81 | - name: Build and push multiarch logical-backup image to ghcr 82 | uses: docker/build-push-action@v3 83 | with: 84 | context: logical-backup 85 | push: true 86 | build-args: BASE_IMAGE=ubuntu:22.04 87 | tags: "${{ steps.image_lb.outputs.BACKUP_IMAGE }}" 88 | platforms: linux/amd64,linux/arm64 89 | -------------------------------------------------------------------------------- /.github/workflows/run_e2e.yaml: -------------------------------------------------------------------------------- 1 | name: operator-e2e-tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | tests: 11 | name: End-2-End tests 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions/setup-go@v2 16 | with: 17 | go-version: "^1.23.4" 18 | - name: Make dependencies 19 | run: make deps mocks 20 | - name: Code generation 21 | run: make codegen 22 | - name: Run unit tests 23 | run: make test 24 | - name: Run end-2-end tests 25 | run: make e2e 26 | -------------------------------------------------------------------------------- /.github/workflows/run_tests.yaml: -------------------------------------------------------------------------------- 1 | name: operator-tests 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | 9 | jobs: 10 | tests: 11 | name: Unit tests and coverage 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions/setup-go@v2 16 | with: 17 | go-version: "^1.23.4" 18 | - name: Make dependencies 19 | run: make deps mocks 20 | - name: Compile 21 | run: make linux 22 | - name: Run unit tests 23 | run: go test -race -covermode atomic -coverprofile=coverage.out ./... 24 | - name: Convert coverage to lcov 25 | uses: jandelgado/gcov2lcov-action@v1.1.1 26 | - name: Coveralls 27 | uses: coverallsapp/github-action@master 28 | with: 29 | github-token: ${{ secrets.GITHUB_TOKEN }} 30 | path-to-lcov: coverage.lcov 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | _manifests 10 | _tmp 11 | github.com 12 | 13 | # Architecture specific extensions/prefixes 14 | *.[568vq] 15 | [568vq].out 16 | 17 | *.cgo1.go 18 | *.cgo2.c 19 | _cgo_defun.c 20 | _cgo_gotypes.go 21 | _cgo_export.* 22 | 23 | _testmain.go 24 | 25 | *.exe 26 | *.test 27 | *.prof 28 | /vendor/ 29 | /kubectl-pg/vendor/ 30 | /build/ 31 | /docker/build/ 32 | /github.com/ 33 | .idea 34 | .vscode 35 | 36 | scm-source.json 37 | 38 | # diagrams 39 | *.aux 40 | *.log 41 | 42 | # Python 43 | # Adapted from https://github.com/github/gitignore/blob/master/Python.gitignore 44 | 45 | # Byte-compiled / optimized / DLL files 46 | __pycache__/ 47 | *.py[cod] 48 | *$py.class 49 | 50 | # Distribution / packaging 51 | .Python 52 | ui/app/node_modules 53 | ui/operator_ui/static/build 54 | build/ 55 | develop-eggs/ 56 | dist/ 57 | downloads/ 58 | eggs/ 59 | .eggs/ 60 | lib/ 61 | lib64/ 62 | parts/ 63 | sdist/ 64 | var/ 65 | wheels/ 66 | pip-wheel-metadata/ 67 | share/python-wheels/ 68 | *.egg-info/ 69 | .installed.cfg 70 | *.egg 71 | MANIFEST 72 | 73 | # PyInstaller 74 | # Usually these files are written by a python script from a template 75 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 76 | *.manifest 77 | *.spec 78 | 79 | # Installer logs 80 | pip-log.txt 81 | pip-delete-this-directory.txt 82 | 83 | # Unit test / coverage reports 84 | htmlcov/ 85 | .tox/ 86 | .nox/ 87 | .coverage 88 | .coverage.* 89 | .cache 90 | nosetests.xml 91 | coverage.xml 92 | *.cover 93 | .hypothesis/ 94 | .pytest_cache/ 95 | 96 | # e2e tests 97 | e2e/manifests 98 | e2e/tls 99 | 100 | # Translations 101 | *.mo 102 | *.pot 103 | 104 | mocks 105 | 106 | ui/.npm/ 107 | 108 | .DS_Store 109 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | # https://github.com/golangci/golangci/wiki/Configuration 2 | 3 | service: 4 | prepare: 5 | - make deps 6 | -------------------------------------------------------------------------------- /.zappr.yaml: -------------------------------------------------------------------------------- 1 | # for github.com 2 | X-Zalando-Team: "acid" 3 | # type should be one of [code, doc, config, tools, secrets] 4 | # code will be the default value, if X-Zalando-Type is not found in .zappr.yml 5 | X-Zalando-Type: code 6 | 7 | approvals: 8 | groups: 9 | zalando: 10 | minimum: 2 11 | from: 12 | orgs: 13 | - zalando -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # global owners 2 | * @sdudoladov @Jan-M @FxKu @jopadi @idanovinda @hughcapet @macedigital 3 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing guidelines 2 | 3 | Wanna contribute to the Postgres Operator? Yay - here is how! 4 | 5 | ## Reporting issues 6 | 7 | Before filing an issue, if you have a question about Postgres Operator or have 8 | a problem using it, please read the [concepts](docs/index.md) page or use the 9 | different guides that we provide for [users](docs/user.md), 10 | [developers](docs/developer.md) or [admins](docs/administrator). Also double 11 | check with the current issues on our [Issues Tracker](https://github.com/zalando/postgres-operator/issues). 12 | 13 | ## Contributing a pull request 14 | 15 | 1. Submit a comment to the relevant issue or create a new issue describing your 16 | proposed change. 17 | 2. Do a fork, develop and test your code changes. 18 | 3. Include documentation 19 | 4. Submit a pull request. 20 | 21 | You'll get feedback about your pull request as soon as possible. 22 | 23 | Happy Operator hacking ;-) 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2024 Zalando SE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTAINERS: -------------------------------------------------------------------------------- 1 | Sergey Dudoladov 2 | Felix Kunde 3 | Jan Mussler 4 | Jociele Padilha 5 | Ida Novindasari 6 | Polina Bungina 7 | Matthias Adler 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean local test linux macos mocks docker push e2e 2 | 3 | BINARY ?= postgres-operator 4 | BUILD_FLAGS ?= -v 5 | CGO_ENABLED ?= 0 6 | ifeq ($(RACE),1) 7 | BUILD_FLAGS += -race -a 8 | CGO_ENABLED=1 9 | endif 10 | 11 | LOCAL_BUILD_FLAGS ?= $(BUILD_FLAGS) 12 | LDFLAGS ?= -X=main.version=$(VERSION) 13 | DOCKERDIR = docker 14 | 15 | IMAGE ?= registry.opensource.zalan.do/acid/$(BINARY) 16 | TAG ?= $(VERSION) 17 | GITHEAD = $(shell git rev-parse --short HEAD) 18 | GITURL = $(shell git config --get remote.origin.url) 19 | GITSTATUS = $(shell git status --porcelain || echo "no changes") 20 | SOURCES = cmd/main.go 21 | VERSION ?= $(shell git describe --tags --always --dirty) 22 | DIRS := cmd pkg 23 | PKG := `go list ./... | grep -v /vendor/` 24 | 25 | ifeq ($(DEBUG),1) 26 | DOCKERFILE = DebugDockerfile 27 | DEBUG_POSTFIX := -debug-$(shell date hhmmss) 28 | BUILD_FLAGS += -gcflags "-N -l" 29 | else 30 | DOCKERFILE = Dockerfile 31 | endif 32 | 33 | ifeq ($(FRESH),1) 34 | DEBUG_FRESH=$(shell date +"%H-%M-%S") 35 | endif 36 | 37 | ifdef CDP_PULL_REQUEST_NUMBER 38 | CDP_TAG := -${CDP_BUILD_VERSION} 39 | endif 40 | 41 | ifndef GOPATH 42 | GOPATH := $(HOME)/go 43 | endif 44 | 45 | PATH := $(GOPATH)/bin:$(PATH) 46 | SHELL := env PATH=$(PATH) $(SHELL) 47 | 48 | default: local 49 | 50 | clean: 51 | rm -rf build 52 | 53 | local: ${SOURCES} 54 | hack/verify-codegen.sh 55 | CGO_ENABLED=${CGO_ENABLED} go build -o build/${BINARY} $(LOCAL_BUILD_FLAGS) -ldflags "$(LDFLAGS)" $^ 56 | 57 | linux: ${SOURCES} 58 | GOOS=linux GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/linux/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $^ 59 | 60 | macos: ${SOURCES} 61 | GOOS=darwin GOARCH=amd64 CGO_ENABLED=${CGO_ENABLED} go build -o build/macos/${BINARY} ${BUILD_FLAGS} -ldflags "$(LDFLAGS)" $^ 62 | 63 | docker: ${DOCKERDIR}/${DOCKERFILE} 64 | echo `(env)` 65 | echo "Tag ${TAG}" 66 | echo "Version ${VERSION}" 67 | echo "CDP tag ${CDP_TAG}" 68 | echo "git describe $(shell git describe --tags --always --dirty)" 69 | docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)$(DEBUG_FRESH)$(DEBUG_POSTFIX)" -f "${DOCKERDIR}/${DOCKERFILE}" --build-arg VERSION="${VERSION}" . 70 | 71 | indocker-race: 72 | docker run --rm -v "${GOPATH}":"${GOPATH}" -e GOPATH="${GOPATH}" -e RACE=1 -w ${PWD} golang:1.23.4 bash -c "make linux" 73 | 74 | push: 75 | docker push "$(IMAGE):$(TAG)$(CDP_TAG)" 76 | 77 | mocks: 78 | GO111MODULE=on go generate ./... 79 | 80 | tools: 81 | GO111MODULE=on go get k8s.io/client-go@kubernetes-1.30.4 82 | GO111MODULE=on go install github.com/golang/mock/mockgen@v1.6.0 83 | GO111MODULE=on go mod tidy 84 | 85 | fmt: 86 | @gofmt -l -w -s $(DIRS) 87 | 88 | vet: 89 | @go vet $(PKG) 90 | @staticcheck $(PKG) 91 | 92 | deps: tools 93 | GO111MODULE=on go mod vendor 94 | 95 | test: 96 | hack/verify-codegen.sh 97 | GO111MODULE=on go test ./... 98 | 99 | codegen: 100 | hack/update-codegen.sh 101 | 102 | e2e: docker # build operator image to be tested 103 | cd e2e; make e2etest 104 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | If you have discovered a security vulnerability, please email tech-security@zalando.de. 4 | -------------------------------------------------------------------------------- /build-ci.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e -x 3 | 4 | team_repo="$GOPATH/src/github.com/zalando/" 5 | project_dir="$team_repo/postgres-operator" 6 | 7 | mkdir -p "$team_repo" 8 | 9 | ln -s "$PWD" "$project_dir" 10 | cd "$project_dir" 11 | 12 | make deps clean docker push 13 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: postgres-operator-ui 3 | version: 1.14.0 4 | appVersion: 1.14.0 5 | home: https://github.com/zalando/postgres-operator 6 | description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience 7 | keywords: 8 | - postgres 9 | - operator 10 | - ui 11 | - cloud-native 12 | - patroni 13 | - spilo 14 | maintainers: 15 | - name: Zalando 16 | email: opensource@zalando.de 17 | sources: 18 | - https://github.com/zalando/postgres-operator 19 | engine: gotpl 20 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/postgres-operator-ui-1.10.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator-ui/postgres-operator-ui-1.10.1.tgz -------------------------------------------------------------------------------- /charts/postgres-operator-ui/postgres-operator-ui-1.11.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator-ui/postgres-operator-ui-1.11.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator-ui/postgres-operator-ui-1.12.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator-ui/postgres-operator-ui-1.12.2.tgz -------------------------------------------------------------------------------- /charts/postgres-operator-ui/postgres-operator-ui-1.13.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator-ui/postgres-operator-ui-1.13.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator-ui/postgres-operator-ui-1.14.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator-ui/postgres-operator-ui-1.14.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator-ui/postgres-operator-ui-1.9.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator-ui/postgres-operator-ui-1.9.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator-ui/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | To verify that postgres-operator has started, run: 2 | 3 | kubectl --namespace={{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ template "postgres-operator-ui.name" . }}" -------------------------------------------------------------------------------- /charts/postgres-operator-ui/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "postgres-operator-ui.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "postgres-operator-ui.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create a service account name. 29 | */}} 30 | {{- define "postgres-operator-ui.serviceAccountName" -}} 31 | {{ default (include "postgres-operator-ui.fullname" .) .Values.serviceAccount.name }} 32 | {{- end -}} 33 | 34 | {{/* 35 | Create chart name and version as used by the chart label. 36 | */}} 37 | {{- define "postgres-operator-ui.chart" -}} 38 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 39 | {{- end -}} 40 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/templates/clusterrole.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.create }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ include "postgres-operator-ui.serviceAccountName" . }} 6 | labels: 7 | app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} 8 | helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | rules: 12 | - apiGroups: 13 | - acid.zalan.do 14 | resources: 15 | - postgresqls 16 | verbs: 17 | - create 18 | - delete 19 | - get 20 | - list 21 | - patch 22 | - update 23 | - apiGroups: 24 | - "" 25 | resources: 26 | - pods 27 | verbs: 28 | - get 29 | - list 30 | - watch 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - services 35 | verbs: 36 | - get 37 | - list 38 | - apiGroups: 39 | - apps 40 | resources: 41 | - deployments 42 | - statefulsets 43 | verbs: 44 | - get 45 | - list 46 | - apiGroups: 47 | - "" 48 | resources: 49 | - namespaces 50 | verbs: 51 | - get 52 | - list 53 | {{ end }} 54 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.create }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: {{ include "postgres-operator-ui.serviceAccountName" . }} 6 | labels: 7 | app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} 8 | helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: {{ include "postgres-operator-ui.serviceAccountName" . }} 15 | subjects: 16 | - kind: ServiceAccount 17 | name: {{ include "postgres-operator-ui.serviceAccountName" . }} 18 | namespace: {{ .Release.Namespace }} 19 | {{ end }} 20 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/templates/ingress.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.ingress.enabled -}} 2 | {{- $fullName := include "postgres-operator-ui.fullname" . -}} 3 | {{- $svcPort := .Values.service.port -}} 4 | 5 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} 6 | apiVersion: networking.k8s.io/v1 7 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 8 | apiVersion: networking.k8s.io/v1beta1 9 | {{- else -}} 10 | apiVersion: extensions/v1beta1 11 | {{- end }} 12 | kind: Ingress 13 | metadata: 14 | name: {{ $fullName }} 15 | namespace: {{ .Release.Namespace }} 16 | labels: 17 | app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} 18 | helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} 19 | app.kubernetes.io/managed-by: {{ .Release.Service }} 20 | app.kubernetes.io/instance: {{ .Release.Name }} 21 | {{- with .Values.ingress.annotations }} 22 | annotations: 23 | {{- toYaml . | nindent 4 }} 24 | {{- end }} 25 | spec: 26 | {{- if .Values.ingress.ingressClassName }} 27 | ingressClassName: {{ .Values.ingress.ingressClassName }} 28 | {{- end }} 29 | {{- if .Values.ingress.tls }} 30 | tls: 31 | {{- range .Values.ingress.tls }} 32 | - hosts: 33 | {{- range .hosts }} 34 | - {{ . | quote }} 35 | {{- end }} 36 | secretName: {{ .secretName }} 37 | {{- end }} 38 | {{- end }} 39 | rules: 40 | {{- range .Values.ingress.hosts }} 41 | - host: {{ .host | quote }} 42 | http: 43 | paths: 44 | {{- range .paths }} 45 | - path: {{ . }} 46 | {{ if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion -}} 47 | pathType: Prefix 48 | backend: 49 | service: 50 | name: {{ $fullName }} 51 | port: 52 | number: {{ $svcPort }} 53 | {{- else -}} 54 | backend: 55 | serviceName: {{ $fullName }} 56 | servicePort: {{ $svcPort }} 57 | {{- end -}} 58 | {{- end }} 59 | {{- end }} 60 | {{- end }} 61 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} 6 | helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} 7 | app.kubernetes.io/managed-by: {{ .Release.Service }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | {{- with .Values.service.annotations }} 10 | annotations: 11 | {{- toYaml . | nindent 4 }} 12 | {{- end }} 13 | name: {{ template "postgres-operator-ui.fullname" . }} 14 | namespace: {{ .Release.Namespace }} 15 | spec: 16 | ports: 17 | - port: {{ .Values.service.port }} 18 | targetPort: 8081 19 | {{- if and (eq .Values.service.type "NodePort") .Values.service.nodePort }} 20 | nodePort: {{ .Values.service.nodePort }} 21 | {{- end }} 22 | protocol: TCP 23 | selector: 24 | app.kubernetes.io/instance: {{ .Release.Name }} 25 | app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} 26 | type: {{ .Values.service.type }} 27 | 28 | 29 | -------------------------------------------------------------------------------- /charts/postgres-operator-ui/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.serviceAccount.create }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "postgres-operator-ui.serviceAccountName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ template "postgres-operator-ui.name" . }} 9 | helm.sh/chart: {{ template "postgres-operator-ui.chart" . }} 10 | app.kubernetes.io/managed-by: {{ .Release.Service }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | {{ end }} 13 | -------------------------------------------------------------------------------- /charts/postgres-operator/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | -------------------------------------------------------------------------------- /charts/postgres-operator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: postgres-operator 3 | version: 1.14.0 4 | appVersion: 1.14.0 5 | home: https://github.com/zalando/postgres-operator 6 | description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes 7 | keywords: 8 | - postgres 9 | - operator 10 | - cloud-native 11 | - patroni 12 | - spilo 13 | maintainers: 14 | - name: Zalando 15 | email: opensource@zalando.de 16 | sources: 17 | - https://github.com/zalando/postgres-operator 18 | engine: gotpl 19 | -------------------------------------------------------------------------------- /charts/postgres-operator/crds/postgresteams.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: postgresteams.acid.zalan.do 5 | labels: 6 | app.kubernetes.io/name: postgres-operator 7 | spec: 8 | group: acid.zalan.do 9 | names: 10 | kind: PostgresTeam 11 | listKind: PostgresTeamList 12 | plural: postgresteams 13 | singular: postgresteam 14 | shortNames: 15 | - pgteam 16 | categories: 17 | - all 18 | scope: Namespaced 19 | versions: 20 | - name: v1 21 | served: true 22 | storage: true 23 | subresources: 24 | status: {} 25 | schema: 26 | openAPIV3Schema: 27 | type: object 28 | required: 29 | - kind 30 | - apiVersion 31 | - spec 32 | properties: 33 | kind: 34 | type: string 35 | enum: 36 | - PostgresTeam 37 | apiVersion: 38 | type: string 39 | enum: 40 | - acid.zalan.do/v1 41 | spec: 42 | type: object 43 | properties: 44 | additionalSuperuserTeams: 45 | type: object 46 | description: "Map for teamId and associated additional superuser teams" 47 | additionalProperties: 48 | type: array 49 | nullable: true 50 | description: "List of teams to become Postgres superusers" 51 | items: 52 | type: string 53 | additionalTeams: 54 | type: object 55 | description: "Map for teamId and associated additional teams" 56 | additionalProperties: 57 | type: array 58 | nullable: true 59 | description: "List of teams whose members will also be added to the Postgres cluster" 60 | items: 61 | type: string 62 | additionalMembers: 63 | type: object 64 | description: "Map for teamId and associated additional users" 65 | additionalProperties: 66 | type: array 67 | nullable: true 68 | description: "List of users who will also be added to the Postgres cluster" 69 | items: 70 | type: string 71 | -------------------------------------------------------------------------------- /charts/postgres-operator/postgres-operator-1.10.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator/postgres-operator-1.10.1.tgz -------------------------------------------------------------------------------- /charts/postgres-operator/postgres-operator-1.11.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator/postgres-operator-1.11.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator/postgres-operator-1.12.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator/postgres-operator-1.12.2.tgz -------------------------------------------------------------------------------- /charts/postgres-operator/postgres-operator-1.13.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator/postgres-operator-1.13.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator/postgres-operator-1.14.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator/postgres-operator-1.14.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator/postgres-operator-1.9.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/charts/postgres-operator/postgres-operator-1.9.0.tgz -------------------------------------------------------------------------------- /charts/postgres-operator/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | To verify that postgres-operator has started, run: 2 | 3 | kubectl --namespace={{ .Release.Namespace }} get pods -l "app.kubernetes.io/name={{ template "postgres-operator.name" . }}" 4 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "postgres-operator.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "postgres-operator.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create a service account name. 29 | */}} 30 | {{- define "postgres-operator.serviceAccountName" -}} 31 | {{ default (include "postgres-operator.fullname" .) .Values.serviceAccount.name }} 32 | {{- end -}} 33 | 34 | {{/* 35 | Create a pod service account name. 36 | */}} 37 | {{- define "postgres-pod.serviceAccountName" -}} 38 | {{ default (printf "%s-%v" (include "postgres-operator.fullname" .) "pod") .Values.podServiceAccount.name }} 39 | {{- end -}} 40 | 41 | {{/* 42 | Create a pod priority class name. 43 | */}} 44 | {{- define "postgres-pod.priorityClassName" -}} 45 | {{ default (printf "%s-%v" (include "postgres-operator.fullname" .) "pod") .Values.podPriorityClassName.name }} 46 | {{- end -}} 47 | 48 | {{/* 49 | Create a controller ID. 50 | */}} 51 | {{- define "postgres-operator.controllerID" -}} 52 | {{ default (include "postgres-operator.fullname" .) .Values.controllerID.name }} 53 | {{- end -}} 54 | 55 | {{/* 56 | Create chart name and version as used by the chart label. 57 | */}} 58 | {{- define "postgres-operator.chart" -}} 59 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 60 | {{- end -}} 61 | 62 | {{/* 63 | Flatten nested config options when ConfigMap is used as ConfigTarget 64 | */}} 65 | {{- define "flattenValuesForConfigMap" }} 66 | {{- range $key, $value := . }} 67 | {{- if kindIs "slice" $value }} 68 | {{ $key }}: {{ join "," $value | quote }} 69 | {{- else if kindIs "map" $value }} 70 | {{- $list := list }} 71 | {{- range $subKey, $subValue := $value }} 72 | {{- $list = append $list (printf "%s:%s" $subKey $subValue) }} 73 | {{- end }} 74 | {{ $key }}: {{ join "," $list | quote }} 75 | {{- else }} 76 | {{ $key }}: {{ $value | quote }} 77 | {{- end }} 78 | {{- end }} 79 | {{- end }} 80 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/clusterrole-postgres-pod.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.create }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: {{ include "postgres-pod.serviceAccountName" . }} 6 | labels: 7 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 8 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | rules: 12 | # Patroni needs to watch and manage config maps or endpoints 13 | {{- if toString .Values.configGeneral.kubernetes_use_configmaps | eq "true" }} 14 | - apiGroups: 15 | - "" 16 | resources: 17 | - configmaps 18 | verbs: 19 | - create 20 | - delete 21 | - deletecollection 22 | - get 23 | - list 24 | - patch 25 | - update 26 | - watch 27 | {{- else }} 28 | - apiGroups: 29 | - "" 30 | resources: 31 | - endpoints 32 | verbs: 33 | - create 34 | - delete 35 | - deletecollection 36 | - get 37 | - list 38 | - patch 39 | - update 40 | - watch 41 | {{- end }} 42 | # Patroni needs to watch pods 43 | - apiGroups: 44 | - "" 45 | resources: 46 | - pods 47 | verbs: 48 | - get 49 | - list 50 | - patch 51 | - update 52 | - watch 53 | # to let Patroni create a headless service 54 | - apiGroups: 55 | - "" 56 | resources: 57 | - services 58 | verbs: 59 | - create 60 | {{- if toString .Values.configKubernetes.spilo_privileged | eq "true" }} 61 | # to run privileged pods 62 | - apiGroups: 63 | - extensions 64 | resources: 65 | - podsecuritypolicies 66 | resourceNames: 67 | - privileged 68 | verbs: 69 | - use 70 | {{- end }} 71 | {{ end }} 72 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/clusterrolebinding.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.create }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRoleBinding 4 | metadata: 5 | name: {{ include "postgres-operator.serviceAccountName" . }} 6 | labels: 7 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 8 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | roleRef: 12 | apiGroup: rbac.authorization.k8s.io 13 | kind: ClusterRole 14 | name: {{ include "postgres-operator.serviceAccountName" . }} 15 | subjects: 16 | - kind: ServiceAccount 17 | name: {{ include "postgres-operator.serviceAccountName" . }} 18 | namespace: {{ .Release.Namespace }} 19 | {{ end }} 20 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/configmap.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.configTarget "ConfigMap" }} 2 | apiVersion: v1 3 | kind: ConfigMap 4 | metadata: 5 | name: {{ template "postgres-operator.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 9 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 10 | app.kubernetes.io/managed-by: {{ .Release.Service }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | data: 13 | {{- if or .Values.podPriorityClassName.create .Values.podPriorityClassName.name }} 14 | pod_priority_class_name: {{ include "postgres-pod.priorityClassName" . }} 15 | {{- end }} 16 | pod_service_account_name: {{ include "postgres-pod.serviceAccountName" . }} 17 | {{- include "flattenValuesForConfigMap" .Values.configGeneral | indent 2 }} 18 | {{- include "flattenValuesForConfigMap" .Values.configUsers | indent 2 }} 19 | {{- include "flattenValuesForConfigMap" .Values.configMajorVersionUpgrade | indent 2 }} 20 | {{- include "flattenValuesForConfigMap" .Values.configKubernetes | indent 2 }} 21 | {{- include "flattenValuesForConfigMap" .Values.configTimeouts | indent 2 }} 22 | {{- include "flattenValuesForConfigMap" .Values.configLoadBalancer | indent 2 }} 23 | {{- include "flattenValuesForConfigMap" .Values.configAwsOrGcp | indent 2 }} 24 | {{- include "flattenValuesForConfigMap" .Values.configLogicalBackup | indent 2 }} 25 | {{- include "flattenValuesForConfigMap" .Values.configDebug | indent 2 }} 26 | {{- include "flattenValuesForConfigMap" .Values.configLoggingRestApi | indent 2 }} 27 | {{- include "flattenValuesForConfigMap" .Values.configTeamsApi | indent 2 }} 28 | {{- include "flattenValuesForConfigMap" .Values.configConnectionPooler | indent 2 }} 29 | {{- include "flattenValuesForConfigMap" .Values.configPatroni | indent 2 }} 30 | {{- end }} 31 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/operatorconfiguration.yaml: -------------------------------------------------------------------------------- 1 | {{- if eq .Values.configTarget "OperatorConfigurationCRD" }} 2 | apiVersion: "acid.zalan.do/v1" 3 | kind: OperatorConfiguration 4 | metadata: 5 | name: {{ template "postgres-operator.fullname" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 9 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 10 | app.kubernetes.io/managed-by: {{ .Release.Service }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | configuration: 13 | {{ tpl (toYaml .Values.configGeneral) . | indent 2 }} 14 | users: 15 | {{ tpl (toYaml .Values.configUsers) . | indent 4 }} 16 | major_version_upgrade: 17 | {{ tpl (toYaml .Values.configMajorVersionUpgrade) . | indent 4 }} 18 | kubernetes: 19 | {{- if .Values.podPriorityClassName.name }} 20 | pod_priority_class_name: {{ .Values.podPriorityClassName.name }} 21 | {{- end }} 22 | pod_service_account_name: {{ include "postgres-pod.serviceAccountName" . }} 23 | oauth_token_secret_name: {{ template "postgres-operator.fullname" . }} 24 | {{ tpl (toYaml .Values.configKubernetes) . | indent 4 }} 25 | postgres_pod_resources: 26 | {{ tpl (toYaml .Values.configPostgresPodResources) . | indent 4 }} 27 | timeouts: 28 | {{ tpl (toYaml .Values.configTimeouts) . | indent 4 }} 29 | load_balancer: 30 | {{ tpl (toYaml .Values.configLoadBalancer) . | indent 4 }} 31 | aws_or_gcp: 32 | {{ tpl (toYaml .Values.configAwsOrGcp) . | indent 4 }} 33 | logical_backup: 34 | {{ tpl (toYaml .Values.configLogicalBackup) . | indent 4 }} 35 | debug: 36 | {{ tpl (toYaml .Values.configDebug) . | indent 4 }} 37 | teams_api: 38 | {{ tpl (toYaml .Values.configTeamsApi) . | indent 4 }} 39 | logging_rest_api: 40 | {{ tpl (toYaml .Values.configLoggingRestApi) . | indent 4 }} 41 | connection_pooler: 42 | {{ tpl (toYaml .Values.configConnectionPooler) . | indent 4 }} 43 | patroni: 44 | {{ tpl (toYaml .Values.configPatroni) . | indent 4 }} 45 | {{- end }} 46 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/postgres-pod-priority-class.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.podPriorityClassName.create }} 2 | apiVersion: scheduling.k8s.io/v1 3 | description: 'Use only for databases controlled by Postgres operator' 4 | kind: PriorityClass 5 | metadata: 6 | labels: 7 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 8 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | name: {{ include "postgres-pod.priorityClassName" . }} 12 | namespace: {{ .Release.Namespace }} 13 | preemptionPolicy: PreemptLowerPriority 14 | globalDefault: false 15 | value: {{ .Values.podPriorityClassName.priority }} 16 | {{- end }} 17 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 6 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 7 | app.kubernetes.io/managed-by: {{ .Release.Service }} 8 | app.kubernetes.io/instance: {{ .Release.Name }} 9 | name: {{ template "postgres-operator.fullname" . }} 10 | namespace: {{ .Release.Namespace }} 11 | spec: 12 | type: ClusterIP 13 | ports: 14 | - port: 8080 15 | protocol: TCP 16 | targetPort: 8080 17 | selector: 18 | app.kubernetes.io/instance: {{ .Release.Name }} 19 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 20 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.serviceAccount.create }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "postgres-operator.serviceAccountName" . }} 6 | namespace: {{ .Release.Namespace }} 7 | labels: 8 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 9 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 10 | app.kubernetes.io/managed-by: {{ .Release.Service }} 11 | app.kubernetes.io/instance: {{ .Release.Name }} 12 | {{ end }} 13 | -------------------------------------------------------------------------------- /charts/postgres-operator/templates/user-facing-clusterroles.yaml: -------------------------------------------------------------------------------- 1 | {{ if .Values.rbac.createAggregateClusterRoles }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | rbac.authorization.k8s.io/aggregate-to-admin: "true" 7 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 8 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 9 | app.kubernetes.io/managed-by: {{ .Release.Service }} 10 | app.kubernetes.io/instance: {{ .Release.Name }} 11 | name: {{ template "postgres-operator.fullname" . }}:users:admin 12 | rules: 13 | - apiGroups: 14 | - acid.zalan.do 15 | resources: 16 | - postgresqls 17 | - postgresqls/status 18 | verbs: 19 | - create 20 | - delete 21 | - deletecollection 22 | - get 23 | - list 24 | - patch 25 | - update 26 | - watch 27 | 28 | --- 29 | apiVersion: rbac.authorization.k8s.io/v1 30 | kind: ClusterRole 31 | metadata: 32 | labels: 33 | rbac.authorization.k8s.io/aggregate-to-edit: "true" 34 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 35 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 36 | app.kubernetes.io/managed-by: {{ .Release.Service }} 37 | app.kubernetes.io/instance: {{ .Release.Name }} 38 | name: {{ template "postgres-operator.fullname" . }}:users:edit 39 | rules: 40 | - apiGroups: 41 | - acid.zalan.do 42 | resources: 43 | - postgresqls 44 | verbs: 45 | - create 46 | - update 47 | - patch 48 | - delete 49 | 50 | --- 51 | apiVersion: rbac.authorization.k8s.io/v1 52 | kind: ClusterRole 53 | metadata: 54 | labels: 55 | rbac.authorization.k8s.io/aggregate-to-view: "true" 56 | app.kubernetes.io/name: {{ template "postgres-operator.name" . }} 57 | helm.sh/chart: {{ template "postgres-operator.chart" . }} 58 | app.kubernetes.io/managed-by: {{ .Release.Service }} 59 | app.kubernetes.io/instance: {{ .Release.Name }} 60 | name: {{ template "postgres-operator.fullname" . }}:users:view 61 | rules: 62 | - apiGroups: 63 | - acid.zalan.do 64 | resources: 65 | - postgresqls 66 | - postgresqls/status 67 | verbs: 68 | - get 69 | - list 70 | - watch 71 | {{ end }} 72 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "os" 6 | "os/signal" 7 | "sync" 8 | "syscall" 9 | "time" 10 | 11 | log "github.com/sirupsen/logrus" 12 | 13 | "github.com/zalando/postgres-operator/pkg/controller" 14 | "github.com/zalando/postgres-operator/pkg/spec" 15 | "github.com/zalando/postgres-operator/pkg/util/k8sutil" 16 | ) 17 | 18 | var ( 19 | kubeConfigFile string 20 | outOfCluster bool 21 | version string 22 | config spec.ControllerConfig 23 | ) 24 | 25 | func mustParseDuration(d string) time.Duration { 26 | duration, err := time.ParseDuration(d) 27 | if err != nil { 28 | panic(err) 29 | } 30 | return duration 31 | } 32 | 33 | func init() { 34 | flag.StringVar(&kubeConfigFile, "kubeconfig", "", "Path to kubeconfig file with authorization and master location information.") 35 | flag.BoolVar(&outOfCluster, "outofcluster", false, "Whether the operator runs in- our outside of the Kubernetes cluster.") 36 | flag.BoolVar(&config.NoDatabaseAccess, "nodatabaseaccess", false, "Disable all access to the database from the operator side.") 37 | flag.BoolVar(&config.NoTeamsAPI, "noteamsapi", false, "Disable all access to the teams API") 38 | flag.IntVar(&config.KubeQPS, "kubeqps", 10, "Kubernetes api requests per second.") 39 | flag.IntVar(&config.KubeBurst, "kubeburst", 20, "Kubernetes api requests burst limit.") 40 | flag.Parse() 41 | 42 | config.EnableJsonLogging = os.Getenv("ENABLE_JSON_LOGGING") == "true" 43 | 44 | configMapRawName := os.Getenv("CONFIG_MAP_NAME") 45 | if configMapRawName != "" { 46 | 47 | err := config.ConfigMapName.Decode(configMapRawName) 48 | if err != nil { 49 | log.Fatalf("incorrect config map name: %v", configMapRawName) 50 | } 51 | 52 | log.Printf("Fully qualified configmap name: %v", config.ConfigMapName) 53 | 54 | } 55 | if crdInterval := os.Getenv("CRD_READY_WAIT_INTERVAL"); crdInterval != "" { 56 | config.CRDReadyWaitInterval = mustParseDuration(crdInterval) 57 | } else { 58 | config.CRDReadyWaitInterval = 4 * time.Second 59 | } 60 | 61 | if crdTimeout := os.Getenv("CRD_READY_WAIT_TIMEOUT"); crdTimeout != "" { 62 | config.CRDReadyWaitTimeout = mustParseDuration(crdTimeout) 63 | } else { 64 | config.CRDReadyWaitTimeout = 30 * time.Second 65 | } 66 | } 67 | 68 | func main() { 69 | var err error 70 | 71 | if config.EnableJsonLogging { 72 | log.SetFormatter(&log.JSONFormatter{}) 73 | } 74 | log.SetOutput(os.Stdout) 75 | log.Printf("Spilo operator %s\n", version) 76 | 77 | sigs := make(chan os.Signal, 1) 78 | stop := make(chan struct{}) 79 | signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) // Push signals into channel 80 | 81 | wg := &sync.WaitGroup{} // Goroutines can add themselves to this to be waited on 82 | 83 | config.RestConfig, err = k8sutil.RestConfig(kubeConfigFile, outOfCluster) 84 | if err != nil { 85 | log.Fatalf("couldn't get REST config: %v", err) 86 | } 87 | 88 | config.RestConfig.QPS = float32(config.KubeQPS) 89 | config.RestConfig.Burst = config.KubeBurst 90 | 91 | c := controller.NewController(&config, "") 92 | 93 | c.Run(stop, wg) 94 | 95 | sig := <-sigs 96 | log.Printf("Shutting down... %+v", sig) 97 | 98 | close(stop) // Tell goroutines to stop themselves 99 | wg.Wait() // Wait for all to be stopped 100 | } 101 | -------------------------------------------------------------------------------- /delivery.yaml: -------------------------------------------------------------------------------- 1 | version: "2017-09-20" 2 | pipeline: 3 | - id: build-postgres-operator 4 | type: script 5 | vm_config: 6 | type: linux 7 | size: large 8 | image: cdp-runtime/go 9 | cache: 10 | paths: 11 | - /go/pkg/mod # pkg cache for Go modules 12 | - ~/.cache/go-build # Go build cache 13 | commands: 14 | - desc: Run unit tests 15 | cmd: | 16 | make deps mocks test 17 | 18 | - desc: Build Docker image 19 | cmd: | 20 | IS_PR_BUILD=${CDP_PULL_REQUEST_NUMBER+"true"} 21 | if [[ ${CDP_TARGET_BRANCH} == "master" && ${IS_PR_BUILD} != "true" ]] 22 | then 23 | IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator 24 | else 25 | IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator-test 26 | fi 27 | export IMAGE 28 | make docker push 29 | 30 | - id: build-operator-ui 31 | type: script 32 | vm_config: 33 | type: linux 34 | 35 | commands: 36 | - desc: 'Prepare environment' 37 | cmd: | 38 | apt-get update 39 | apt-get install -y build-essential 40 | 41 | - desc: 'Compile JavaScript app' 42 | cmd: | 43 | cd ui 44 | make appjs 45 | 46 | - desc: 'Build and push Docker image' 47 | cmd: | 48 | cd ui 49 | IS_PR_BUILD=${CDP_PULL_REQUEST_NUMBER+"true"} 50 | if [[ ${CDP_TARGET_BRANCH} == "master" && ${IS_PR_BUILD} != "true" ]] 51 | then 52 | IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator-ui 53 | else 54 | IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator-ui-test 55 | fi 56 | export IMAGE 57 | make docker 58 | make push 59 | 60 | - id: build-logical-backup 61 | type: script 62 | vm_config: 63 | type: linux 64 | 65 | commands: 66 | - desc: Build image 67 | cmd: | 68 | cd logical-backup 69 | export TAG=$(git describe --tags --always --dirty) 70 | IMAGE="registry-write.opensource.zalan.do/acid/logical-backup" 71 | docker build --rm -t "$IMAGE:$TAG$CDP_TAG" . 72 | docker push "$IMAGE:$TAG$CDP_TAG" 73 | -------------------------------------------------------------------------------- /docker/DebugDockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23-alpine 2 | LABEL maintainer="Team ACID @ Zalando " 3 | 4 | # We need root certificates to deal with teams api over https 5 | RUN apk -U add --no-cache ca-certificates delve 6 | 7 | COPY build/* / 8 | 9 | RUN addgroup -g 1000 pgo 10 | RUN adduser -D -u 1000 -G pgo -g 'Postgres Operator' pgo 11 | 12 | USER pgo:pgo 13 | RUN ls -l / 14 | 15 | CMD ["/dlv", "--listen=:7777", "--headless=true", "--api-version=2", "exec", "/postgres-operator"] 16 | -------------------------------------------------------------------------------- /docker/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=registry.opensource.zalan.do/library/alpine-3:latest 2 | FROM golang:1.23-alpine AS builder 3 | ARG VERSION=latest 4 | 5 | COPY . /go/src/github.com/zalando/postgres-operator 6 | WORKDIR /go/src/github.com/zalando/postgres-operator 7 | 8 | RUN GO111MODULE=on go mod vendor \ 9 | && CGO_ENABLED=0 go build -o build/postgres-operator -v -ldflags "-X=main.version=${VERSION}" cmd/main.go 10 | 11 | FROM ${BASE_IMAGE} 12 | LABEL maintainer="Team ACID @ Zalando " 13 | LABEL org.opencontainers.image.source="https://github.com/zalando/postgres-operator" 14 | 15 | # We need root certificates to deal with teams api over https 16 | RUN apk -U upgrade --no-cache \ 17 | && apk add --no-cache curl ca-certificates 18 | 19 | COPY --from=builder /go/src/github.com/zalando/postgres-operator/build/* / 20 | 21 | RUN addgroup -g 1000 pgo 22 | RUN adduser -D -u 1000 -G pgo -g 'Postgres Operator' pgo 23 | 24 | USER 1000:1000 25 | 26 | ENTRYPOINT ["/postgres-operator"] 27 | -------------------------------------------------------------------------------- /docker/build_operator.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export DEBIAN_FRONTEND=noninteractive 4 | 5 | arch=$(dpkg --print-architecture) 6 | 7 | set -ex 8 | 9 | # Install dependencies 10 | 11 | apt-get update 12 | apt-get install -y wget 13 | 14 | ( 15 | cd /tmp 16 | wget -q "https://storage.googleapis.com/golang/go1.23.4.linux-${arch}.tar.gz" -O go.tar.gz 17 | tar -xf go.tar.gz 18 | mv go /usr/local 19 | ln -s /usr/local/go/bin/go /usr/bin/go 20 | go version 21 | ) 22 | 23 | # Build 24 | 25 | export PATH="$PATH:$HOME/go/bin" 26 | export GOPATH="$HOME/go" 27 | mkdir -p build 28 | 29 | GO111MODULE=on go mod vendor 30 | CGO_ENABLED=0 go build -o build/postgres-operator -v -ldflags "$OPERATOR_LDFLAGS" cmd/main.go 31 | -------------------------------------------------------------------------------- /docs/diagrams/Makefile: -------------------------------------------------------------------------------- 1 | OBJ=$(patsubst %.tex, %.png, $(wildcard *.tex)) 2 | 3 | .PHONY: all 4 | 5 | all: $(OBJ) 6 | 7 | %.pdf: %.tex 8 | lualatex $< -shell-escape $@ 9 | 10 | %.png: %.pdf 11 | convert -flatten -density 300 $< -quality 90 $@ 12 | -------------------------------------------------------------------------------- /docs/diagrams/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/logo.png -------------------------------------------------------------------------------- /docs/diagrams/neutral_operator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/neutral_operator.png -------------------------------------------------------------------------------- /docs/diagrams/neutral_operator_dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/neutral_operator_dark.png -------------------------------------------------------------------------------- /docs/diagrams/neutral_operator_light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/neutral_operator_light.png -------------------------------------------------------------------------------- /docs/diagrams/operator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/operator.png -------------------------------------------------------------------------------- /docs/diagrams/pgui-cluster-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pgui-cluster-list.png -------------------------------------------------------------------------------- /docs/diagrams/pgui-cluster-startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pgui-cluster-startup.png -------------------------------------------------------------------------------- /docs/diagrams/pgui-delete-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pgui-delete-cluster.png -------------------------------------------------------------------------------- /docs/diagrams/pgui-finished-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pgui-finished-setup.png -------------------------------------------------------------------------------- /docs/diagrams/pgui-new-cluster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pgui-new-cluster.png -------------------------------------------------------------------------------- /docs/diagrams/pgui-operator-logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pgui-operator-logs.png -------------------------------------------------------------------------------- /docs/diagrams/pgui-waiting-for-master.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pgui-waiting-for-master.png -------------------------------------------------------------------------------- /docs/diagrams/pod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/docs/diagrams/pod.png -------------------------------------------------------------------------------- /docs/reference/command_line_and_environment.md: -------------------------------------------------------------------------------- 1 | # Command-line options 2 | 3 | The following command-line options are supported for the operator: 4 | 5 | * **-kubeconfig** 6 | the path to the kubeconfig file. Usually named config, it contains 7 | authorization information as well as the URL of the Kubernetes master. 8 | 9 | * **-outofcluster** 10 | run the operator on a client machine, as opposed to a within the cluster. 11 | When running in this mode, the operator cannot connect to databases inside 12 | the cluster, as well as call URLs of in-cluster objects (i.e. teams api 13 | server). Mostly useful for debugging, it also requires setting the 14 | `OPERATOR_NAMESPACE` environment variable for the operator own namespace. 15 | 16 | * **-nodatabaseaccess** 17 | disable database access from the operator. Equivalent to the 18 | `enable_database_access` set to off and can be overridden by the 19 | aforementioned operator configuration option. 20 | 21 | * **-noteamsapi** 22 | disable access to the teams API. Equivalent to the `enable_teams_api` set to 23 | off can can be overridden by the aforementioned operator configuration 24 | option. 25 | 26 | In addition to that, standard [glog 27 | flags](https://godoc.org/github.com/golang/glog) are also supported. For 28 | instance, one may want to add `-alsologtostderr` and `-v=8` to debug the 29 | operator REST calls. 30 | 31 | # Environment variables 32 | 33 | The following environment variables are accepted by the operator: 34 | 35 | * **CONFIG_MAP_NAME** 36 | name of the config map where the operator should look for its configuration. 37 | Must be present. 38 | 39 | * **OPERATOR_NAMESPACE** 40 | name of the namespace the operator runs it. Overrides autodetection by the 41 | operator itself. 42 | 43 | * **WATCHED_NAMESPACE** 44 | the name of the namespace the operator watches. Special '*' character denotes 45 | all namespaces. Empty value defaults to the operator namespace. Overrides the 46 | `watched_namespace` operator parameter. 47 | 48 | * **SCALYR_API_KEY** (*deprecated*) 49 | the value of the Scalyr API key to supply to the pods. Overrides the 50 | `scalyr_api_key` operator parameter. 51 | 52 | * **CRD_READY_WAIT_TIMEOUT** 53 | defines the timeout for the complete `postgresql` CRD creation. When not set 54 | default is 30s. 55 | 56 | * **CRD_READY_WAIT_INTERVAL** 57 | defines the interval between consecutive attempts waiting for the 58 | `postgresql` CRD to be created. The default is 5s. 59 | 60 | * **ENABLE_JSON_LOGGING** 61 | Set to `true` for JSON formatted logging output. 62 | The default is false. 63 | -------------------------------------------------------------------------------- /e2e/Dockerfile: -------------------------------------------------------------------------------- 1 | # An image to run e2e tests. 2 | # The image does not include the tests; all necessary files are bind-mounted when a container starts. 3 | FROM ubuntu:20.04 4 | LABEL maintainer="Team ACID @ Zalando " 5 | 6 | ENV TERM xterm-256color 7 | 8 | COPY requirements.txt ./ 9 | 10 | RUN apt-get update \ 11 | && apt-get install --no-install-recommends -y \ 12 | python3 \ 13 | python3-setuptools \ 14 | python3-pip \ 15 | curl \ 16 | vim \ 17 | && pip3 install --no-cache-dir -r requirements.txt \ 18 | && curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.24.3/bin/linux/amd64/kubectl \ 19 | && chmod +x ./kubectl \ 20 | && mv ./kubectl /usr/local/bin/kubectl \ 21 | && apt-get clean \ 22 | && rm -rf /var/lib/apt/lists/* 23 | 24 | # working line 25 | # python3 -m unittest discover -v --failfast -k test_e2e.EndToEndTestCase.test_lazy_spilo_upgrade --start-directory tests 26 | ENTRYPOINT ["python3", "-m", "unittest"] 27 | CMD ["discover","-v","--failfast","--start-directory","/tests"] -------------------------------------------------------------------------------- /e2e/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean copy docker push tools test 2 | 3 | BINARY ?= postgres-operator-e2e-tests-runner 4 | BUILD_FLAGS ?= -v 5 | CGO_ENABLED ?= 0 6 | ifeq ($(RACE),1) 7 | BUILD_FLAGS += -race -a 8 | CGO_ENABLED=1 9 | endif 10 | 11 | LOCAL_BUILD_FLAGS ?= $(BUILD_FLAGS) 12 | LDFLAGS ?= -X=main.version=$(VERSION) 13 | 14 | IMAGE ?= registry.opensource.zalan.do/acid/$(BINARY) 15 | VERSION ?= $(shell git describe --tags --always --dirty) 16 | TAG ?= $(VERSION) 17 | GITHEAD = $(shell git rev-parse --short HEAD) 18 | GITURL = $(shell git config --get remote.origin.url) 19 | GITSTATU = $(shell git status --porcelain || echo 'no changes') 20 | TTYFLAGS = $(shell test -t 0 && echo '-it') 21 | 22 | ifndef GOPATH 23 | GOPATH := $(HOME)/go 24 | endif 25 | 26 | PATH := $(GOPATH)/bin:$(PATH) 27 | 28 | default: tools 29 | 30 | clean: 31 | rm -rf manifests 32 | rm -rf tls 33 | 34 | copy: clean 35 | mkdir manifests 36 | cp -r ../manifests . 37 | mkdir tls 38 | 39 | docker: 40 | docker build -t "$(IMAGE):$(TAG)" . 41 | 42 | push: docker 43 | docker push "$(IMAGE):$(TAG)" 44 | 45 | tools: 46 | # install pinned version of 'kind' 47 | # go install must run outside of a dir with a (module-based) Go project ! 48 | # otherwise go install updates project's dependencies and/or behaves differently 49 | cd "/tmp" && GO111MODULE=on go install sigs.k8s.io/kind@v0.24.0 50 | 51 | e2etest: tools copy clean 52 | ./run.sh main 53 | 54 | cleanup: clean 55 | ./run.sh cleanup -------------------------------------------------------------------------------- /e2e/exec.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | kubectl exec -i $1 -- sh -c "$2" 3 | -------------------------------------------------------------------------------- /e2e/exec_into_env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export cluster_name="postgres-operator-e2e-tests" 4 | export kubeconfig_path="/tmp/kind-config-${cluster_name}" 5 | export operator_image="registry.opensource.zalan.do/acid/postgres-operator:latest" 6 | export e2e_test_runner_image="registry.opensource.zalan.do/acid/postgres-operator-e2e-tests-runner:0.4" 7 | 8 | docker run -it --entrypoint /bin/bash --network=host -e "TERM=xterm-256color" \ 9 | --mount type=bind,source="$(readlink -f ${kubeconfig_path})",target=/root/.kube/config \ 10 | --mount type=bind,source="$(readlink -f manifests)",target=/manifests \ 11 | --mount type=bind,source="$(readlink -f tests)",target=/tests \ 12 | --mount type=bind,source="$(readlink -f exec.sh)",target=/exec.sh \ 13 | --mount type=bind,source="$(readlink -f scripts)",target=/scripts \ 14 | -e OPERATOR_IMAGE="${operator_image}" "${e2e_test_runner_image}" 15 | -------------------------------------------------------------------------------- /e2e/kind-cluster-postgres-operator-e2e-tests.yaml: -------------------------------------------------------------------------------- 1 | kind: Cluster 2 | apiVersion: kind.x-k8s.io/v1alpha4 3 | nodes: 4 | - role: control-plane 5 | - role: worker 6 | - role: worker 7 | featureGates: 8 | StatefulSetAutoDeletePVC: true 9 | -------------------------------------------------------------------------------- /e2e/requirements.txt: -------------------------------------------------------------------------------- 1 | kubernetes==29.2.0 2 | timeout_decorator==0.5.0 3 | pyyaml==6.0.1 4 | -------------------------------------------------------------------------------- /e2e/scripts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | kubectl delete postgresql acid-minimal-cluster 3 | kubectl delete deployments -l application=db-connection-pooler,cluster-name=acid-minimal-cluster 4 | kubectl delete statefulsets -l application=spilo,cluster-name=acid-minimal-cluster 5 | kubectl delete services -l application=spilo,cluster-name=acid-minimal-cluster 6 | kubectl delete configmap postgres-operator 7 | kubectl delete deployment postgres-operator -------------------------------------------------------------------------------- /e2e/scripts/get_logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | kubectl logs $(kubectl get pods -l name=postgres-operator --field-selector status.phase=Running -o jsonpath='{.items..metadata.name}') 3 | -------------------------------------------------------------------------------- /e2e/scripts/watch_objects.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | watch -c " 4 | kubectl get postgresql --all-namespaces 5 | echo 6 | echo -n 'Rolling upgrade pending: ' 7 | kubectl get pods -o jsonpath='{.items[].metadata.annotations.zalando-postgres-operator-rolling-update-required}' 8 | echo 9 | echo 10 | echo 'Pods' 11 | kubectl get pods -l application=spilo -o wide --all-namespaces 12 | echo 13 | kubectl get pods -l application=db-connection-pooler -o wide --all-namespaces 14 | echo 15 | echo 'Statefulsets' 16 | kubectl get statefulsets --all-namespaces 17 | echo 18 | echo 'Deployments' 19 | kubectl get deployments --all-namespaces -l application=db-connection-pooler 20 | kubectl get deployments --all-namespaces -l application=postgres-operator 21 | echo 22 | echo 23 | echo 'Step from operator deployment' 24 | kubectl get pods -l name=postgres-operator -o jsonpath='{.items..metadata.annotations.step}' 25 | echo 26 | echo 27 | echo 'Spilo Image in statefulset' 28 | kubectl get pods -l application=spilo -o jsonpath='{.items..spec.containers..image}' 29 | echo 30 | echo 31 | echo 'Queue Status' 32 | kubectl exec -it \$(kubectl get pods -l name=postgres-operator -o jsonpath='{.items..metadata.name}') -- curl localhost:8080/workers/all/status/ 33 | echo" -------------------------------------------------------------------------------- /e2e/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # This version is replaced during release process. 2 | __version__ = '2019.0.dev1' 3 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/zalando/postgres-operator 2 | 3 | go 1.23.4 4 | 5 | require ( 6 | github.com/aws/aws-sdk-go v1.53.8 7 | github.com/golang/mock v1.6.0 8 | github.com/lib/pq v1.10.9 9 | github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d 10 | github.com/pkg/errors v0.9.1 11 | github.com/r3labs/diff v1.1.0 12 | github.com/sirupsen/logrus v1.9.3 13 | github.com/stretchr/testify v1.9.0 14 | golang.org/x/crypto v0.31.0 15 | golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 16 | gopkg.in/yaml.v2 v2.4.0 17 | k8s.io/api v0.30.4 18 | k8s.io/apiextensions-apiserver v0.25.9 19 | k8s.io/apimachinery v0.30.4 20 | k8s.io/client-go v0.30.4 21 | k8s.io/code-generator v0.25.9 22 | ) 23 | 24 | require ( 25 | github.com/Masterminds/semver v1.5.0 26 | github.com/davecgh/go-spew v1.1.1 // indirect 27 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 28 | github.com/evanphx/json-patch v4.12.0+incompatible // indirect 29 | github.com/go-logr/logr v1.4.1 // indirect 30 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 31 | github.com/go-openapi/jsonreference v0.20.2 // indirect 32 | github.com/go-openapi/swag v0.22.3 // indirect 33 | github.com/gogo/protobuf v1.3.2 // indirect 34 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect 35 | github.com/golang/protobuf v1.5.4 // indirect 36 | github.com/google/gnostic-models v0.6.8 // indirect 37 | github.com/google/go-cmp v0.6.0 // indirect 38 | github.com/google/gofuzz v1.2.0 // indirect 39 | github.com/google/uuid v1.3.0 // indirect 40 | github.com/gorilla/websocket v1.5.0 // indirect 41 | github.com/imdario/mergo v0.3.6 // indirect 42 | github.com/jmespath/go-jmespath v0.4.0 // indirect 43 | github.com/josharian/intern v1.0.0 // indirect 44 | github.com/json-iterator/go v1.1.12 // indirect 45 | github.com/kr/text v0.2.0 // indirect 46 | github.com/mailru/easyjson v0.7.7 // indirect 47 | github.com/moby/spdystream v0.2.0 // indirect 48 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 49 | github.com/modern-go/reflect2 v1.0.2 // indirect 50 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 51 | github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect 52 | github.com/pmezard/go-difflib v1.0.0 // indirect 53 | github.com/spf13/pflag v1.0.5 // indirect 54 | golang.org/x/mod v0.17.0 // indirect 55 | golang.org/x/net v0.25.0 // indirect 56 | golang.org/x/oauth2 v0.10.0 // indirect 57 | golang.org/x/sync v0.10.0 // indirect 58 | golang.org/x/sys v0.28.0 // indirect 59 | golang.org/x/term v0.27.0 // indirect 60 | golang.org/x/text v0.21.0 // indirect 61 | golang.org/x/time v0.3.0 // indirect 62 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 63 | google.golang.org/appengine v1.6.7 // indirect 64 | google.golang.org/protobuf v1.33.0 // indirect 65 | gopkg.in/inf.v0 v0.9.1 // indirect 66 | gopkg.in/yaml.v3 v3.0.1 // indirect 67 | k8s.io/gengo v0.0.0-20220902162205-c0856e24416d // indirect 68 | k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect 69 | k8s.io/klog/v2 v2.120.1 // indirect 70 | k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect 71 | k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect 72 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 73 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect 74 | sigs.k8s.io/yaml v1.3.0 // indirect 75 | ) 76 | -------------------------------------------------------------------------------- /hack/custom-boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright YEAR Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | -------------------------------------------------------------------------------- /hack/tools.go: -------------------------------------------------------------------------------- 1 | // +build tools 2 | 3 | /* 4 | Copyright 2019 The Kubernetes Authors. 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | */ 15 | 16 | // This package imports things required by build scripts, to force `go mod` to see them as dependencies 17 | package tools 18 | 19 | import _ "k8s.io/code-generator" 20 | -------------------------------------------------------------------------------- /hack/update-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | GENERATED_PACKAGE_ROOT="github.com" 8 | OPERATOR_PACKAGE_ROOT="${GENERATED_PACKAGE_ROOT}/zalando/postgres-operator" 9 | SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/.. 10 | TARGET_CODE_DIR=${1-${SCRIPT_ROOT}/pkg} 11 | CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${SCRIPT_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo "${GOPATH}"/src/k8s.io/code-generator)} 12 | 13 | cleanup() { 14 | rm -rf "${GENERATED_PACKAGE_ROOT}" 15 | } 16 | trap "cleanup" EXIT SIGINT 17 | 18 | bash "${CODEGEN_PKG}/generate-groups.sh" client,deepcopy,informer,lister \ 19 | "${OPERATOR_PACKAGE_ROOT}/pkg/generated" "${OPERATOR_PACKAGE_ROOT}/pkg/apis" \ 20 | "acid.zalan.do:v1 zalando.org:v1" \ 21 | --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt \ 22 | -o ./ 23 | 24 | cp -r "${OPERATOR_PACKAGE_ROOT}"/pkg/* "${TARGET_CODE_DIR}" 25 | 26 | cleanup 27 | -------------------------------------------------------------------------------- /hack/verify-codegen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | set -o pipefail 6 | 7 | SCRIPT_ROOT=$(dirname "${BASH_SOURCE}")/.. 8 | DIFFROOT="${SCRIPT_ROOT}/pkg" 9 | TMP_DIFFROOT="${SCRIPT_ROOT}/_tmp/pkg" 10 | _tmp="${SCRIPT_ROOT}/_tmp" 11 | 12 | cleanup() { 13 | rm -rf "${_tmp}" 14 | } 15 | trap "cleanup" EXIT SIGINT 16 | 17 | cleanup 18 | 19 | mkdir -p "${TMP_DIFFROOT}" 20 | cp -a "${DIFFROOT}"/* "${TMP_DIFFROOT}" 21 | 22 | "${SCRIPT_ROOT}/hack/update-codegen.sh" "${TMP_DIFFROOT}" 23 | echo "diffing ${DIFFROOT} against freshly generated codegen" 24 | ret=0 25 | diff -Naupr "${DIFFROOT}" "${TMP_DIFFROOT}" || ret=$? 26 | if [[ $ret -eq 0 ]] 27 | then 28 | echo "${DIFFROOT} up to date." 29 | else 30 | echo "${DIFFROOT} is out of date. Please run 'make codegen'" 31 | exit 1 32 | fi 33 | -------------------------------------------------------------------------------- /kubectl-pg/build.sh: -------------------------------------------------------------------------------- 1 | 2 | VERSION=1.0 3 | sed -i "s/KubectlPgVersion string = \"[^\"]*\"/KubectlPgVersion string = \"${VERSION}\"/" cmd/version.go 4 | go install -------------------------------------------------------------------------------- /kubectl-pg/cmd/check.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 Vineeth Pothulapati 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "context" 27 | "fmt" 28 | "log" 29 | 30 | "github.com/spf13/cobra" 31 | postgresConstants "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 32 | v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 33 | apiextv1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1" 34 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 35 | ) 36 | 37 | // checkCmd represent kubectl pg check. 38 | var checkCmd = &cobra.Command{ 39 | Use: "check", 40 | Short: "Checks the Postgres operator is installed in the k8s cluster", 41 | Long: `Checks that the Postgres CRD is registered in a k8s cluster. 42 | This means that the operator pod was able to start normally.`, 43 | Run: func(cmd *cobra.Command, args []string) { 44 | check() 45 | }, 46 | Example: ` 47 | kubectl pg check 48 | `, 49 | } 50 | 51 | // check validates postgresql CRD registered or not. 52 | func check() *v1.CustomResourceDefinition { 53 | config := getConfig() 54 | apiExtClient, err := apiextv1.NewForConfig(config) 55 | if err != nil { 56 | log.Fatal(err) 57 | } 58 | 59 | crdInfo, err := apiExtClient.CustomResourceDefinitions().Get(context.TODO(), postgresConstants.PostgresCRDResouceName, metav1.GetOptions{}) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | 64 | if crdInfo.Name == postgresConstants.PostgresCRDResouceName { 65 | fmt.Printf("Postgres Operator is installed in the k8s cluster.\n") 66 | } else { 67 | fmt.Printf("Postgres Operator is not installed in the k8s cluster.\n") 68 | } 69 | return crdInfo 70 | } 71 | 72 | func init() { 73 | rootCmd.AddCommand(checkCmd) 74 | } 75 | -------------------------------------------------------------------------------- /kubectl-pg/cmd/create.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 Vineeth Pothulapati 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "context" 27 | "fmt" 28 | "log" 29 | "os" 30 | 31 | "github.com/spf13/cobra" 32 | v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 33 | PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" 34 | "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme" 35 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 | ) 37 | 38 | // createCmd kubectl pg create. 39 | var createCmd = &cobra.Command{ 40 | Use: "create", 41 | Short: "Creates postgres object using manifest file", 42 | Long: `Creates postgres custom resource objects from a manifest file.`, 43 | Run: func(cmd *cobra.Command, args []string) { 44 | fileName, _ := cmd.Flags().GetString("file") 45 | create(fileName) 46 | }, 47 | Example: ` 48 | kubectl pg create -f cluster-manifest.yaml 49 | `, 50 | } 51 | 52 | // Create postgresql resources. 53 | func create(fileName string) { 54 | config := getConfig() 55 | postgresConfig, err := PostgresqlLister.NewForConfig(config) 56 | if err != nil { 57 | log.Fatal(err) 58 | } 59 | ymlFile, err := os.ReadFile(fileName) 60 | if err != nil { 61 | log.Fatal(err) 62 | } 63 | 64 | decode := scheme.Codecs.UniversalDeserializer().Decode 65 | obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{}) 66 | if err != nil { 67 | log.Fatal(err) 68 | } 69 | 70 | postgresSql := obj.(*v1.Postgresql) 71 | _, err = postgresConfig.Postgresqls(postgresSql.Namespace).Create(context.TODO(), postgresSql, metav1.CreateOptions{}) 72 | if err != nil { 73 | log.Fatal(err) 74 | } 75 | 76 | fmt.Printf("postgresql %s created.\n", postgresSql.Name) 77 | } 78 | 79 | func init() { 80 | createCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.") 81 | rootCmd.AddCommand(createCmd) 82 | } 83 | -------------------------------------------------------------------------------- /kubectl-pg/cmd/root.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 Vineeth Pothulapati 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "fmt" 27 | "os" 28 | 29 | "github.com/spf13/cobra" 30 | "github.com/spf13/viper" 31 | ) 32 | 33 | var rootCmd = &cobra.Command{ 34 | Use: "kubectl-pg", 35 | Short: "kubectl plugin for the Zalando Postgres operator.", 36 | Long: `kubectl pg plugin for interaction with Zalando postgres operator.`, 37 | } 38 | 39 | // Execute adds all child commands to the root command and sets flags appropriately. 40 | // This is called by main.main(). It only needs to happen once to the rootCmd. 41 | func Execute() { 42 | if err := rootCmd.Execute(); err != nil { 43 | fmt.Println(err) 44 | os.Exit(1) 45 | } 46 | } 47 | 48 | func init() { 49 | viper.SetDefault("author", "Vineeth Pothulapati ") 50 | viper.SetDefault("license", "mit") 51 | } 52 | -------------------------------------------------------------------------------- /kubectl-pg/cmd/version.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 Vineeth Pothulapati 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | package cmd 24 | 25 | import ( 26 | "fmt" 27 | "log" 28 | "strings" 29 | 30 | "github.com/spf13/cobra" 31 | "k8s.io/client-go/kubernetes" 32 | ) 33 | 34 | var KubectlPgVersion string = "1.0" 35 | 36 | // versionCmd represents the version command 37 | var versionCmd = &cobra.Command{ 38 | Use: "version", 39 | Short: "version of kubectl-pg & postgres-operator", 40 | Long: `version of kubectl-pg and current running postgres-operator`, 41 | Run: func(cmd *cobra.Command, args []string) { 42 | namespace, err := cmd.Flags().GetString("namespace") 43 | if err != nil { 44 | log.Fatal(err) 45 | } 46 | version(namespace) 47 | }, 48 | Example: ` 49 | #Lists the version of kubectl pg plugin and postgres operator in current namespace 50 | kubectl pg version 51 | 52 | #Lists the version of kubectl pg plugin and postgres operator in provided namespace 53 | kubectl pg version -n namespace01 54 | `, 55 | } 56 | 57 | func version(namespace string) { 58 | fmt.Printf("kubectl-pg: %s\n", KubectlPgVersion) 59 | 60 | config := getConfig() 61 | client, err := kubernetes.NewForConfig(config) 62 | if err != nil { 63 | log.Fatal(err) 64 | } 65 | 66 | operatorDeployment := getPostgresOperator(client) 67 | if operatorDeployment.Name == "" { 68 | log.Fatal("make sure zalando's postgres operator is running") 69 | } 70 | operatorImage := operatorDeployment.Spec.Template.Spec.Containers[0].Image 71 | imageDetails := strings.Split(operatorImage, ":") 72 | imageSplit := len(imageDetails) 73 | imageVersion := imageDetails[imageSplit-1] 74 | fmt.Printf("Postgres-Operator: %s\n", imageVersion) 75 | } 76 | 77 | func init() { 78 | rootCmd.AddCommand(versionCmd) 79 | versionCmd.Flags().StringP("namespace", "n", DefaultNamespace, "provide the namespace.") 80 | } 81 | -------------------------------------------------------------------------------- /kubectl-pg/main.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright © 2019 Vineeth Pothulapati 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | package main 24 | 25 | import ( 26 | "github.com/zalando/postgres-operator/kubectl-pg/cmd" 27 | ) 28 | 29 | func main() { 30 | cmd.Execute() 31 | } 32 | -------------------------------------------------------------------------------- /logical-backup/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=registry.opensource.zalan.do/library/ubuntu-22.04:latest 2 | FROM ${BASE_IMAGE} 3 | LABEL maintainer="Team ACID @ Zalando " 4 | 5 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 6 | RUN apt-get update \ 7 | && apt-get install --no-install-recommends -y \ 8 | apt-utils \ 9 | ca-certificates \ 10 | lsb-release \ 11 | pigz \ 12 | python3-pip \ 13 | python3-setuptools \ 14 | curl \ 15 | jq \ 16 | gnupg \ 17 | gcc \ 18 | libffi-dev \ 19 | && curl -sL https://aka.ms/InstallAzureCLIDeb | bash \ 20 | && pip3 install --upgrade pip \ 21 | && pip3 install --no-cache-dir awscli --upgrade \ 22 | && pip3 install --no-cache-dir gsutil --upgrade \ 23 | && echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list \ 24 | && cat /etc/apt/sources.list.d/pgdg.list \ 25 | && curl --silent https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - \ 26 | && apt-get update \ 27 | && apt-get install --no-install-recommends -y \ 28 | postgresql-client-17 \ 29 | postgresql-client-16 \ 30 | postgresql-client-15 \ 31 | postgresql-client-14 \ 32 | postgresql-client-13 \ 33 | && apt-get clean \ 34 | && rm -rf /var/lib/apt/lists/* 35 | 36 | COPY dump.sh ./ 37 | 38 | ENV PG_DIR=/usr/lib/postgresql 39 | 40 | ENTRYPOINT ["/dump.sh"] 41 | -------------------------------------------------------------------------------- /manifests/api-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: postgres-operator 5 | spec: 6 | type: ClusterIP 7 | ports: 8 | - port: 8080 9 | protocol: TCP 10 | targetPort: 8080 11 | selector: 12 | name: postgres-operator 13 | -------------------------------------------------------------------------------- /manifests/custom-team-membership.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "acid.zalan.do/v1" 2 | kind: PostgresTeam 3 | metadata: 4 | name: custom-team-membership 5 | spec: 6 | additionalSuperuserTeams: 7 | acid: 8 | - "postgres_superusers" 9 | additionalTeams: 10 | acid: [] 11 | additionalMembers: 12 | acid: 13 | - "elephant" 14 | -------------------------------------------------------------------------------- /manifests/e2e-storage-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: storage.k8s.io/v1 2 | kind: StorageClass 3 | metadata: 4 | name: standard 5 | annotations: 6 | storageclass.kubernetes.io/is-default-class: "true" 7 | provisioner: kubernetes.io/host-path 8 | -------------------------------------------------------------------------------- /manifests/fake-teams-api.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: fake-teams-api 5 | spec: 6 | replicas: 1 7 | selector: 8 | matchLabels: 9 | name: fake-teams-api 10 | template: 11 | metadata: 12 | labels: 13 | name: fake-teams-api 14 | spec: 15 | containers: 16 | - name: fake-teams-api 17 | image: ikitiki/fake-teams-api:latest 18 | 19 | --- 20 | 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: fake-teams-api 25 | spec: 26 | selector: 27 | name: fake-teams-api 28 | ports: 29 | - name: server 30 | port: 80 31 | protocol: TCP 32 | targetPort: 80 33 | type: NodePort 34 | 35 | --- 36 | 37 | apiVersion: v1 38 | kind: Secret 39 | metadata: 40 | name: postgresql-operator 41 | namespace: default 42 | type: Opaque 43 | data: 44 | read-only-token-secret: dGVzdHRva2Vu 45 | read-only-token-type: QmVhcmVy 46 | -------------------------------------------------------------------------------- /manifests/fes.crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: fabriceventstreams.zalando.org 5 | spec: 6 | group: zalando.org 7 | names: 8 | kind: FabricEventStream 9 | listKind: FabricEventStreamList 10 | plural: fabriceventstreams 11 | singular: fabriceventstream 12 | shortNames: 13 | - fes 14 | categories: 15 | - all 16 | scope: Namespaced 17 | versions: 18 | - name: v1 19 | served: true 20 | storage: true 21 | schema: 22 | openAPIV3Schema: 23 | type: object 24 | -------------------------------------------------------------------------------- /manifests/infrastructure-roles-configmap.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ConfigMap 3 | metadata: 4 | name: postgresql-infrastructure-roles 5 | data: 6 | batman: | 7 | inrole: [admin] # following roles will be assigned to the new user 8 | user_flags: 9 | - createdb 10 | db_parameters: # db parameters, applyed for this particular user 11 | log_statement: all 12 | -------------------------------------------------------------------------------- /manifests/infrastructure-roles-new.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | # infrastructure role definition in the new format 4 | # robot_zmon_acid_monitoring_new 5 | user: cm9ib3Rfem1vbl9hY2lkX21vbml0b3JpbmdfbmV3 6 | # foobar_new 7 | password: Zm9vYmFyX25ldw== 8 | kind: Secret 9 | metadata: 10 | name: postgresql-infrastructure-roles-new 11 | type: Opaque 12 | -------------------------------------------------------------------------------- /manifests/infrastructure-roles.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | # required format (w/o quotes): 'propertyNumber: value' 4 | # allowed properties: 'user', 'password', 'inrole' 5 | # numbers >= 1 are mandatory 6 | # alternatively, supply the user: password pairs and 7 | # provide other options in the configmap. 8 | # robot_zmon_acid_monitoring 9 | user1: cm9ib3Rfem1vbl9hY2lkX21vbml0b3Jpbmc= 10 | # foobar 11 | password1: Zm9vYmFy 12 | # robot_zmon 13 | inrole1: cm9ib3Rfem1vbg== 14 | # testuser 15 | user2: dGVzdHVzZXI= 16 | # testpassword 17 | password2: dGVzdHBhc3N3b3Jk 18 | # user batman with the password justice 19 | # look for other fields in the infrastructure roles configmap 20 | batman: anVzdGljZQ== 21 | kind: Secret 22 | metadata: 23 | name: postgresql-infrastructure-roles 24 | type: Opaque 25 | -------------------------------------------------------------------------------- /manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - configmap.yaml 5 | - operator-service-account-rbac.yaml 6 | - postgres-operator.yaml 7 | - api-service.yaml 8 | -------------------------------------------------------------------------------- /manifests/minimal-fake-pooler-deployment.yaml: -------------------------------------------------------------------------------- 1 | # will not run but is good enough for tests to fail 2 | apiVersion: apps/v1 3 | kind: Deployment 4 | metadata: 5 | name: acid-minimal-cluster-pooler 6 | labels: 7 | application: db-connection-pooler 8 | connection-pooler: acid-minimal-cluster-pooler 9 | spec: 10 | replicas: 1 11 | selector: 12 | matchLabels: 13 | application: db-connection-pooler 14 | connection-pooler: acid-minimal-cluster-pooler 15 | cluster-name: acid-minimal-cluster 16 | template: 17 | metadata: 18 | labels: 19 | application: db-connection-pooler 20 | connection-pooler: acid-minimal-cluster-pooler 21 | cluster-name: acid-minimal-cluster 22 | spec: 23 | serviceAccountName: postgres-operator 24 | containers: 25 | - name: postgres-operator 26 | image: registry.opensource.zalan.do/acid/pgbouncer:master-32 27 | imagePullPolicy: IfNotPresent 28 | resources: 29 | requests: 30 | cpu: 100m 31 | memory: 250Mi 32 | limits: 33 | cpu: 500m 34 | memory: 500Mi 35 | env: [] 36 | -------------------------------------------------------------------------------- /manifests/minimal-postgres-lowest-version-manifest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "acid.zalan.do/v1" 2 | kind: postgresql 3 | metadata: 4 | name: acid-upgrade-test 5 | spec: 6 | teamId: "acid" 7 | volume: 8 | size: 1Gi 9 | numberOfInstances: 2 10 | users: 11 | zalando: # database owner 12 | - superuser 13 | - createdb 14 | foo_user: [] # role for application foo 15 | databases: 16 | foo: zalando # dbname: owner 17 | preparedDatabases: 18 | bar: {} 19 | postgresql: 20 | version: "13" 21 | -------------------------------------------------------------------------------- /manifests/minimal-postgres-manifest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "acid.zalan.do/v1" 2 | kind: postgresql 3 | metadata: 4 | name: acid-minimal-cluster 5 | spec: 6 | teamId: "acid" 7 | volume: 8 | size: 1Gi 9 | numberOfInstances: 2 10 | users: 11 | zalando: # database owner 12 | - superuser 13 | - createdb 14 | foo_user: [] # role for application foo 15 | databases: 16 | foo: zalando # dbname: owner 17 | preparedDatabases: 18 | bar: {} 19 | postgresql: 20 | version: "17" 21 | -------------------------------------------------------------------------------- /manifests/platform-credentials.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "zalando.org/v1" 2 | kind: PlatformCredentialsSet 3 | metadata: 4 | name: postgresql-operator 5 | spec: 6 | application: postgresql-operator 7 | tokens: 8 | read-only: 9 | privileges: 10 | cluster-registry-rw: 11 | privileges: 12 | cluster-rw: 13 | privileges: 14 | -------------------------------------------------------------------------------- /manifests/postgres-operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: postgres-operator 5 | labels: 6 | application: postgres-operator 7 | spec: 8 | replicas: 1 9 | strategy: 10 | type: "Recreate" 11 | selector: 12 | matchLabels: 13 | name: postgres-operator 14 | template: 15 | metadata: 16 | labels: 17 | name: postgres-operator 18 | spec: 19 | serviceAccountName: postgres-operator 20 | containers: 21 | - name: postgres-operator 22 | image: ghcr.io/zalando/postgres-operator:v1.14.0 23 | imagePullPolicy: IfNotPresent 24 | resources: 25 | requests: 26 | cpu: 100m 27 | memory: 250Mi 28 | limits: 29 | cpu: 500m 30 | memory: 500Mi 31 | securityContext: 32 | runAsUser: 1000 33 | runAsNonRoot: true 34 | readOnlyRootFilesystem: true 35 | allowPrivilegeEscalation: false 36 | env: 37 | # provided additional ENV vars can overwrite individual config map entries 38 | - name: CONFIG_MAP_NAME 39 | value: "postgres-operator" 40 | # In order to use the CRD OperatorConfiguration instead, uncomment these lines and comment out the two lines above 41 | # - name: POSTGRES_OPERATOR_CONFIGURATION_OBJECT 42 | # value: postgresql-operator-default-configuration 43 | # Define an ID to isolate controllers from each other 44 | # - name: CONTROLLER_ID 45 | # value: "second-operator" 46 | -------------------------------------------------------------------------------- /manifests/postgres-pod-priority-class.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: scheduling.k8s.io/v1 2 | description: 'This priority class must be used only for databases controlled by the 3 | Postgres operator' 4 | kind: PriorityClass 5 | metadata: 6 | labels: 7 | application: postgres-operator 8 | name: postgres-pod-priority 9 | preemptionPolicy: PreemptLowerPriority 10 | globalDefault: false 11 | value: 1000000 12 | -------------------------------------------------------------------------------- /manifests/postgresteam.crd.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: postgresteams.acid.zalan.do 5 | spec: 6 | group: acid.zalan.do 7 | names: 8 | kind: PostgresTeam 9 | listKind: PostgresTeamList 10 | plural: postgresteams 11 | singular: postgresteam 12 | shortNames: 13 | - pgteam 14 | categories: 15 | - all 16 | scope: Namespaced 17 | versions: 18 | - name: v1 19 | served: true 20 | storage: true 21 | subresources: 22 | status: {} 23 | schema: 24 | openAPIV3Schema: 25 | type: object 26 | required: 27 | - kind 28 | - apiVersion 29 | - spec 30 | properties: 31 | kind: 32 | type: string 33 | enum: 34 | - PostgresTeam 35 | apiVersion: 36 | type: string 37 | enum: 38 | - acid.zalan.do/v1 39 | spec: 40 | type: object 41 | properties: 42 | additionalSuperuserTeams: 43 | type: object 44 | description: "Map for teamId and associated additional superuser teams" 45 | additionalProperties: 46 | type: array 47 | nullable: true 48 | description: "List of teams to become Postgres superusers" 49 | items: 50 | type: string 51 | additionalTeams: 52 | type: object 53 | description: "Map for teamId and associated additional teams" 54 | additionalProperties: 55 | type: array 56 | nullable: true 57 | description: "List of teams whose members will also be added to the Postgres cluster" 58 | items: 59 | type: string 60 | additionalMembers: 61 | type: object 62 | description: "Map for teamId and associated additional users" 63 | additionalProperties: 64 | type: array 65 | nullable: true 66 | description: "List of users who will also be added to the Postgres cluster" 67 | items: 68 | type: string 69 | -------------------------------------------------------------------------------- /manifests/standby-manifest.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "acid.zalan.do/v1" 2 | kind: postgresql 3 | metadata: 4 | name: acid-standby-cluster 5 | spec: 6 | teamId: "acid" 7 | volume: 8 | size: 1Gi 9 | numberOfInstances: 1 10 | postgresql: 11 | version: "17" 12 | # Make this a standby cluster and provide either the s3 bucket path of source cluster or the remote primary host for continuous streaming. 13 | standby: 14 | # s3_wal_path: "s3://mybucket/spilo/acid-minimal-cluster/abcd1234-2a4b-4b2a-8c9c-c1234defg567/wal/14/" 15 | standby_host: "acid-minimal-cluster.default" 16 | # standby_port: "5432" 17 | -------------------------------------------------------------------------------- /manifests/user-facing-clusterroles.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | labels: 5 | rbac.authorization.k8s.io/aggregate-to-admin: "true" 6 | name: zalando-postgres-operator:users:admin 7 | rules: 8 | - apiGroups: 9 | - acid.zalan.do 10 | resources: 11 | - postgresqls 12 | - postgresqls/status 13 | verbs: 14 | - create 15 | - delete 16 | - deletecollection 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | 23 | --- 24 | apiVersion: rbac.authorization.k8s.io/v1 25 | kind: ClusterRole 26 | metadata: 27 | labels: 28 | rbac.authorization.k8s.io/aggregate-to-edit: "true" 29 | name: zalando-postgres-operator:users:edit 30 | rules: 31 | - apiGroups: 32 | - acid.zalan.do 33 | resources: 34 | - postgresqls 35 | verbs: 36 | - create 37 | - update 38 | - patch 39 | - delete 40 | 41 | --- 42 | apiVersion: rbac.authorization.k8s.io/v1 43 | kind: ClusterRole 44 | metadata: 45 | labels: 46 | rbac.authorization.k8s.io/aggregate-to-view: "true" 47 | name: zalando-postgres-operator:users:view 48 | rules: 49 | - apiGroups: 50 | - acid.zalan.do 51 | resources: 52 | - postgresqls 53 | - postgresqls/status 54 | verbs: 55 | - get 56 | - list 57 | - watch 58 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Postgres Operator 2 | repo_url: https://github.com/zalando/postgres-operator 3 | theme: readthedocs 4 | 5 | nav: 6 | - Concepts: 'index.md' 7 | - Quickstart: 'quickstart.md' 8 | - Postgres Operator UI: 'operator-ui.md' 9 | - Admin guide: 'administrator.md' 10 | - User guide: 'user.md' 11 | - Developer guide: 'developer.md' 12 | - Reference: 13 | - Config parameters: 'reference/operator_parameters.md' 14 | - Manifest parameters: 'reference/cluster_manifest.md' 15 | - CLI options and environment: 'reference/command_line_and_environment.md' 16 | -------------------------------------------------------------------------------- /mocks/mocks.go: -------------------------------------------------------------------------------- 1 | package mocks 2 | -------------------------------------------------------------------------------- /pkg/apis/acid.zalan.do/register.go: -------------------------------------------------------------------------------- 1 | package acidzalando 2 | 3 | const ( 4 | // GroupName is the group name for the operator CRDs 5 | GroupName = "acid.zalan.do" 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/apis/acid.zalan.do/v1/const.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | // ClusterStatusUnknown etc : status of a Postgres cluster known to the operator 4 | const ( 5 | ClusterStatusUnknown = "" 6 | ClusterStatusCreating = "Creating" 7 | ClusterStatusUpdating = "Updating" 8 | ClusterStatusUpdateFailed = "UpdateFailed" 9 | ClusterStatusSyncFailed = "SyncFailed" 10 | ClusterStatusAddFailed = "CreateFailed" 11 | ClusterStatusRunning = "Running" 12 | ClusterStatusInvalid = "Invalid" 13 | ) 14 | 15 | const ( 16 | serviceNameMaxLength = 63 17 | clusterNameMaxLength = serviceNameMaxLength - len("-repl") 18 | serviceNameRegexString = `^[a-z]([-a-z0-9]*[a-z0-9])?$` 19 | ) 20 | -------------------------------------------------------------------------------- /pkg/apis/acid.zalan.do/v1/doc.go: -------------------------------------------------------------------------------- 1 | // Package v1 is the v1 version of the API. 2 | // +k8s:deepcopy-gen=package,register 3 | 4 | // +groupName=acid.zalan.do 5 | 6 | package v1 7 | -------------------------------------------------------------------------------- /pkg/apis/acid.zalan.do/v1/postgres_team_type.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | ) 6 | 7 | // +genclient 8 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 9 | 10 | // PostgresTeam defines Custom Resource Definition Object for team management. 11 | type PostgresTeam struct { 12 | metav1.TypeMeta `json:",inline"` 13 | metav1.ObjectMeta `json:"metadata,omitempty"` 14 | 15 | Spec PostgresTeamSpec `json:"spec"` 16 | } 17 | 18 | // PostgresTeamSpec defines the specification for the PostgresTeam TPR. 19 | type PostgresTeamSpec struct { 20 | AdditionalSuperuserTeams map[string][]string `json:"additionalSuperuserTeams,omitempty"` 21 | AdditionalTeams map[string][]string `json:"additionalTeams,omitempty"` 22 | AdditionalMembers map[string][]string `json:"additionalMembers,omitempty"` 23 | } 24 | 25 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 26 | 27 | // PostgresTeamList defines a list of PostgresTeam definitions. 28 | type PostgresTeamList struct { 29 | metav1.TypeMeta `json:",inline"` 30 | metav1.ListMeta `json:"metadata"` 31 | 32 | Items []PostgresTeam `json:"items"` 33 | } 34 | -------------------------------------------------------------------------------- /pkg/apis/acid.zalan.do/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | "k8s.io/apimachinery/pkg/runtime" 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | ) 9 | 10 | // APIVersion of the `postgresql` and `operator` CRDs 11 | const ( 12 | APIVersion = "v1" 13 | ) 14 | 15 | var ( 16 | // localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes. 17 | 18 | // SchemeBuilder : An instance of runtime.SchemeBuilder, global for this package 19 | SchemeBuilder runtime.SchemeBuilder 20 | localSchemeBuilder = &SchemeBuilder 21 | //AddToScheme is localSchemeBuilder.AddToScheme 22 | AddToScheme = localSchemeBuilder.AddToScheme 23 | //SchemeGroupVersion has GroupName and APIVersion 24 | SchemeGroupVersion = schema.GroupVersion{Group: acidzalando.GroupName, Version: APIVersion} 25 | ) 26 | 27 | func init() { 28 | // We only register manually written functions here. The registration of the 29 | // generated functions takes place in the generated files. The separation 30 | // makes the code compile even when the generated files are missing. 31 | localSchemeBuilder.Register(addKnownTypes) 32 | } 33 | 34 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 35 | func Resource(resource string) schema.GroupResource { 36 | return SchemeGroupVersion.WithResource(resource).GroupResource() 37 | } 38 | 39 | // Adds the list of known types to api.Scheme. 40 | func addKnownTypes(scheme *runtime.Scheme) error { 41 | // AddKnownType assumes derives the type kind from the type name, which is always uppercase. 42 | // For our CRDs we use lowercase names historically, therefore we have to supply the name separately. 43 | // TODO: User uppercase CRDResourceKind of our types in the next major API version 44 | scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("postgresql"), &Postgresql{}) 45 | scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("postgresqlList"), &PostgresqlList{}) 46 | scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("PostgresTeam"), &PostgresTeam{}) 47 | scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("PostgresTeamList"), &PostgresTeamList{}) 48 | scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("OperatorConfiguration"), 49 | &OperatorConfiguration{}) 50 | scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("OperatorConfigurationList"), 51 | &OperatorConfigurationList{}) 52 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /pkg/apis/zalando.org/register.go: -------------------------------------------------------------------------------- 1 | package zalando 2 | 3 | const ( 4 | // GroupName is the group name for the operator CRDs 5 | GroupName = "zalando.org" 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/apis/zalando.org/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "github.com/zalando/postgres-operator/pkg/apis/zalando.org" 5 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 6 | "k8s.io/apimachinery/pkg/runtime" 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "k8s.io/client-go/kubernetes/scheme" 9 | ) 10 | 11 | // APIVersion of the `fabriceventstream` CRD 12 | const ( 13 | APIVersion = "v1" 14 | ) 15 | 16 | var ( 17 | schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 18 | AddToScheme = schemeBuilder.AddToScheme 19 | ) 20 | 21 | func init() { 22 | err := AddToScheme(scheme.Scheme) 23 | if err != nil { 24 | panic(err) 25 | } 26 | } 27 | 28 | // SchemeGroupVersion is the group version used to register these objects. 29 | var SchemeGroupVersion = schema.GroupVersion{Group: zalando.GroupName, Version: APIVersion} 30 | 31 | // Resource takes an unqualified resource and returns a Group-qualified GroupResource. 32 | func Resource(resource string) schema.GroupResource { 33 | return SchemeGroupVersion.WithResource(resource).GroupResource() 34 | } 35 | 36 | // addKnownTypes adds the set of types defined in this package to the supplied scheme. 37 | func addKnownTypes(scheme *runtime.Scheme) error { 38 | scheme.AddKnownTypes(SchemeGroupVersion, 39 | &FabricEventStream{}, 40 | &FabricEventStreamList{}, 41 | ) 42 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /pkg/apiserver/apiserver_test.go: -------------------------------------------------------------------------------- 1 | package apiserver 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | const ( 8 | clusterStatusTest = "/clusters/test-namespace/testcluster/" 9 | clusterStatusNumericTest = "/clusters/test-namespace-1/testcluster/" 10 | clusterLogsTest = "/clusters/test-namespace/testcluster/logs/" 11 | teamTest = "/clusters/test-id/" 12 | ) 13 | 14 | func TestUrlRegexps(t *testing.T) { 15 | if clusterStatusURL.FindStringSubmatch(clusterStatusTest) == nil { 16 | t.Errorf("clusterStatusURL can't match %s", clusterStatusTest) 17 | } 18 | 19 | if clusterStatusURL.FindStringSubmatch(clusterStatusNumericTest) == nil { 20 | t.Errorf("clusterStatusURL can't match %s", clusterStatusNumericTest) 21 | } 22 | 23 | if clusterLogsURL.FindStringSubmatch(clusterLogsTest) == nil { 24 | t.Errorf("clusterLogsURL can't match %s", clusterLogsTest) 25 | } 26 | 27 | if teamURL.FindStringSubmatch(teamTest) == nil { 28 | t.Errorf("teamURL can't match %s", teamTest) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/cluster/connection_pooler_new_test.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "testing" 5 | 6 | "context" 7 | 8 | appsv1 "k8s.io/api/apps/v1" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | 11 | "k8s.io/apimachinery/pkg/labels" 12 | 13 | "k8s.io/client-go/kubernetes/fake" 14 | ) 15 | 16 | func TestFakeClient(t *testing.T) { 17 | clientSet := fake.NewSimpleClientset() 18 | namespace := "default" 19 | 20 | l := labels.Set(map[string]string{ 21 | "application": "spilo", 22 | }) 23 | 24 | deployment := &appsv1.Deployment{ 25 | ObjectMeta: metav1.ObjectMeta{ 26 | Name: "my-deployment1", 27 | Namespace: namespace, 28 | Labels: l, 29 | }, 30 | } 31 | 32 | clientSet.AppsV1().Deployments(namespace).Create(context.TODO(), deployment, metav1.CreateOptions{}) 33 | 34 | deployment2, _ := clientSet.AppsV1().Deployments(namespace).Get(context.TODO(), "my-deployment1", metav1.GetOptions{}) 35 | 36 | if deployment.ObjectMeta.Name != deployment2.ObjectMeta.Name { 37 | t.Errorf("Deployments are not equal") 38 | } 39 | 40 | deployments, _ := clientSet.AppsV1().Deployments(namespace).List(context.TODO(), metav1.ListOptions{LabelSelector: "application=spilo"}) 41 | 42 | if len(deployments.Items) != 1 { 43 | t.Errorf("Label search does not work") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /pkg/cluster/exec.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "bytes" 5 | "context" 6 | "fmt" 7 | "strings" 8 | 9 | v1 "k8s.io/api/core/v1" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/client-go/kubernetes/scheme" 12 | "k8s.io/client-go/tools/remotecommand" 13 | 14 | "github.com/zalando/postgres-operator/pkg/spec" 15 | "github.com/zalando/postgres-operator/pkg/util/constants" 16 | ) 17 | 18 | // ExecCommand executes arbitrary command inside the pod 19 | func (c *Cluster) ExecCommand(podName *spec.NamespacedName, command ...string) (string, error) { 20 | c.setProcessName("executing command %q", strings.Join(command, " ")) 21 | 22 | var ( 23 | execOut bytes.Buffer 24 | execErr bytes.Buffer 25 | ) 26 | 27 | pod, err := c.KubeClient.Pods(podName.Namespace).Get(context.TODO(), podName.Name, metav1.GetOptions{}) 28 | if err != nil { 29 | return "", fmt.Errorf("could not get pod info: %v", err) 30 | } 31 | 32 | // iterate through all containers looking for the one running PostgreSQL. 33 | targetContainer := -1 34 | for i, cr := range pod.Spec.Containers { 35 | if cr.Name == constants.PostgresContainerName { 36 | targetContainer = i 37 | break 38 | } 39 | } 40 | 41 | if targetContainer < 0 { 42 | return "", fmt.Errorf("could not find %s container to exec to", constants.PostgresContainerName) 43 | } 44 | 45 | req := c.KubeClient.RESTClient.Post(). 46 | Resource("pods"). 47 | Name(podName.Name). 48 | Namespace(podName.Namespace). 49 | SubResource("exec") 50 | req.VersionedParams(&v1.PodExecOptions{ 51 | Container: pod.Spec.Containers[targetContainer].Name, 52 | Command: command, 53 | Stdout: true, 54 | Stderr: true, 55 | }, scheme.ParameterCodec) 56 | 57 | exec, err := remotecommand.NewSPDYExecutor(c.RestConfig, "POST", req.URL()) 58 | if err != nil { 59 | return "", fmt.Errorf("failed to init executor: %v", err) 60 | } 61 | 62 | err = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{ 63 | Stdout: &execOut, 64 | Stderr: &execErr, 65 | Tty: false, 66 | }) 67 | 68 | if err != nil { 69 | return "", fmt.Errorf("could not execute: %v", err) 70 | } 71 | 72 | if execErr.Len() > 0 { 73 | return "", fmt.Errorf("stderr: %v", execErr.String()) 74 | } 75 | 76 | return execOut.String(), nil 77 | } 78 | -------------------------------------------------------------------------------- /pkg/cluster/filesystems.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/zalando/postgres-operator/pkg/spec" 8 | "github.com/zalando/postgres-operator/pkg/util/constants" 9 | "github.com/zalando/postgres-operator/pkg/util/filesystems" 10 | ) 11 | 12 | func (c *Cluster) getPostgresFilesystemInfo(podName *spec.NamespacedName) (device, fstype string, err error) { 13 | out, err := c.ExecCommand(podName, "bash", "-c", fmt.Sprintf("df -T %s|tail -1", constants.PostgresDataMount)) 14 | if err != nil { 15 | return "", "", err 16 | } 17 | fields := strings.Fields(out) 18 | if len(fields) < 2 { 19 | return "", "", fmt.Errorf("too few fields in the df output") 20 | } 21 | 22 | return fields[0], fields[1], nil 23 | } 24 | 25 | func (c *Cluster) resizePostgresFilesystem(podName *spec.NamespacedName, resizers []filesystems.FilesystemResizer) error { 26 | // resize2fs always writes to stderr, and ExecCommand considers a non-empty stderr an error 27 | // first, determine the device and the filesystem 28 | deviceName, fsType, err := c.getPostgresFilesystemInfo(podName) 29 | if err != nil { 30 | return fmt.Errorf("could not get device and type for the postgres filesystem: %v", err) 31 | } 32 | for _, resizer := range resizers { 33 | if !resizer.CanResizeFilesystem(fsType) { 34 | continue 35 | } 36 | err := resizer.ResizeFilesystem(deviceName, func(cmd string) (out string, err error) { 37 | return c.ExecCommand(podName, "bash", "-c", cmd) 38 | }) 39 | 40 | return err 41 | } 42 | return fmt.Errorf("could not resize filesystem: no compatible resizers for the filesystem of type %q", fsType) 43 | } 44 | -------------------------------------------------------------------------------- /pkg/cluster/types.go: -------------------------------------------------------------------------------- 1 | package cluster 2 | 3 | import ( 4 | "time" 5 | 6 | acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 7 | appsv1 "k8s.io/api/apps/v1" 8 | v1 "k8s.io/api/core/v1" 9 | policyv1 "k8s.io/api/policy/v1" 10 | "k8s.io/apimachinery/pkg/types" 11 | ) 12 | 13 | // PostgresRole describes role of the node 14 | type PostgresRole string 15 | 16 | const ( 17 | // spilo roles 18 | Master PostgresRole = "master" 19 | Replica PostgresRole = "replica" 20 | Patroni PostgresRole = "config" 21 | 22 | // roles returned by Patroni cluster endpoint 23 | Leader PostgresRole = "leader" 24 | StandbyLeader PostgresRole = "standby_leader" 25 | SyncStandby PostgresRole = "sync_standby" 26 | ) 27 | 28 | // PodEventType represents the type of a pod-related event 29 | type PodEventType string 30 | 31 | // Possible values for the EventType 32 | const ( 33 | PodEventAdd PodEventType = "ADD" 34 | PodEventUpdate PodEventType = "UPDATE" 35 | PodEventDelete PodEventType = "DELETE" 36 | ) 37 | 38 | // PodEvent describes the event for a single Pod 39 | type PodEvent struct { 40 | ResourceVersion string 41 | PodName types.NamespacedName 42 | PrevPod *v1.Pod 43 | CurPod *v1.Pod 44 | EventType PodEventType 45 | } 46 | 47 | // Process describes process of the cluster 48 | type Process struct { 49 | Name string 50 | StartTime time.Time 51 | } 52 | 53 | // WorkerStatus describes status of the worker 54 | type WorkerStatus struct { 55 | CurrentCluster types.NamespacedName 56 | CurrentProcess Process 57 | } 58 | 59 | // ClusterStatus describes status of the cluster 60 | type ClusterStatus struct { 61 | Team string 62 | Cluster string 63 | Namespace string 64 | MasterService *v1.Service 65 | ReplicaService *v1.Service 66 | MasterEndpoint *v1.Endpoints 67 | ReplicaEndpoint *v1.Endpoints 68 | StatefulSet *appsv1.StatefulSet 69 | PrimaryPodDisruptionBudget *policyv1.PodDisruptionBudget 70 | CriticalOpPodDisruptionBudget *policyv1.PodDisruptionBudget 71 | 72 | CurrentProcess Process 73 | Worker uint32 74 | Status acidv1.PostgresStatus 75 | Spec acidv1.PostgresSpec 76 | Error error 77 | } 78 | 79 | type TemplateParams map[string]interface{} 80 | 81 | type InstallFunction func(schema string, user string) error 82 | 83 | type SyncReason []string 84 | 85 | // no sync happened, empty value 86 | var NoSync SyncReason = []string{} 87 | -------------------------------------------------------------------------------- /pkg/controller/node_test.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/zalando/postgres-operator/pkg/spec" 7 | v1 "k8s.io/api/core/v1" 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | const ( 12 | readyLabel = "lifecycle-status" 13 | readyValue = "ready" 14 | ) 15 | 16 | func newNodeTestController() *Controller { 17 | var controller = NewController(&spec.ControllerConfig{}, "node-test") 18 | return controller 19 | } 20 | 21 | func makeNode(labels map[string]string, isSchedulable bool) *v1.Node { 22 | return &v1.Node{ 23 | ObjectMeta: metav1.ObjectMeta{ 24 | Namespace: v1.NamespaceDefault, 25 | Labels: labels, 26 | }, 27 | Spec: v1.NodeSpec{ 28 | Unschedulable: !isSchedulable, 29 | }, 30 | } 31 | } 32 | 33 | var nodeTestController = newNodeTestController() 34 | 35 | func TestNodeIsReady(t *testing.T) { 36 | testName := "TestNodeIsReady" 37 | var testTable = []struct { 38 | in *v1.Node 39 | out bool 40 | readinessLabel map[string]string 41 | }{ 42 | { 43 | in: makeNode(map[string]string{"foo": "bar"}, true), 44 | out: true, 45 | readinessLabel: map[string]string{readyLabel: readyValue}, 46 | }, 47 | { 48 | in: makeNode(map[string]string{"foo": "bar"}, false), 49 | out: false, 50 | readinessLabel: map[string]string{readyLabel: readyValue}, 51 | }, 52 | { 53 | in: makeNode(map[string]string{readyLabel: readyValue}, false), 54 | out: true, 55 | readinessLabel: map[string]string{readyLabel: readyValue}, 56 | }, 57 | { 58 | in: makeNode(map[string]string{"foo": "bar", "master": "true"}, false), 59 | out: true, 60 | readinessLabel: map[string]string{readyLabel: readyValue}, 61 | }, 62 | { 63 | in: makeNode(map[string]string{"foo": "bar", "master": "true"}, false), 64 | out: true, 65 | readinessLabel: map[string]string{readyLabel: readyValue}, 66 | }, 67 | { 68 | in: makeNode(map[string]string{"foo": "bar"}, true), 69 | out: true, 70 | readinessLabel: map[string]string{}, 71 | }, 72 | { 73 | in: makeNode(map[string]string{"foo": "bar"}, false), 74 | out: false, 75 | readinessLabel: map[string]string{}, 76 | }, 77 | { 78 | in: makeNode(map[string]string{readyLabel: readyValue}, false), 79 | out: false, 80 | readinessLabel: map[string]string{}, 81 | }, 82 | { 83 | in: makeNode(map[string]string{"foo": "bar", "master": "true"}, false), 84 | out: true, 85 | readinessLabel: map[string]string{}, 86 | }, 87 | } 88 | for _, tt := range testTable { 89 | nodeTestController.opConfig.NodeReadinessLabel = tt.readinessLabel 90 | if isReady := nodeTestController.nodeIsReady(tt.in); isReady != tt.out { 91 | t.Errorf("%s: expected response %t does not match the actual %t for the node %#v", 92 | testName, tt.out, isReady, tt.in) 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /pkg/controller/pod.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "context" 5 | 6 | v1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/runtime" 9 | "k8s.io/apimachinery/pkg/watch" 10 | 11 | "github.com/zalando/postgres-operator/pkg/cluster" 12 | "github.com/zalando/postgres-operator/pkg/spec" 13 | "github.com/zalando/postgres-operator/pkg/util" 14 | "k8s.io/apimachinery/pkg/types" 15 | ) 16 | 17 | func (c *Controller) podListFunc(options metav1.ListOptions) (runtime.Object, error) { 18 | opts := metav1.ListOptions{ 19 | Watch: options.Watch, 20 | ResourceVersion: options.ResourceVersion, 21 | TimeoutSeconds: options.TimeoutSeconds, 22 | } 23 | 24 | return c.KubeClient.Pods(c.opConfig.WatchedNamespace).List(context.TODO(), opts) 25 | } 26 | 27 | func (c *Controller) podWatchFunc(options metav1.ListOptions) (watch.Interface, error) { 28 | opts := metav1.ListOptions{ 29 | Watch: options.Watch, 30 | ResourceVersion: options.ResourceVersion, 31 | TimeoutSeconds: options.TimeoutSeconds, 32 | } 33 | 34 | return c.KubeClient.Pods(c.opConfig.WatchedNamespace).Watch(context.TODO(), opts) 35 | } 36 | 37 | func (c *Controller) dispatchPodEvent(clusterName spec.NamespacedName, event cluster.PodEvent) { 38 | c.clustersMu.RLock() 39 | cluster, ok := c.clusters[clusterName] 40 | c.clustersMu.RUnlock() 41 | if ok { 42 | cluster.ReceivePodEvent(event) 43 | } 44 | } 45 | 46 | func (c *Controller) podAdd(obj interface{}) { 47 | if pod, ok := obj.(*v1.Pod); ok { 48 | c.preparePodEventForDispatch(pod, nil, cluster.PodEventAdd) 49 | } 50 | } 51 | 52 | func (c *Controller) podUpdate(prev, cur interface{}) { 53 | prevPod, ok := prev.(*v1.Pod) 54 | if !ok { 55 | return 56 | } 57 | 58 | curPod, ok := cur.(*v1.Pod) 59 | if !ok { 60 | return 61 | } 62 | 63 | c.preparePodEventForDispatch(curPod, prevPod, cluster.PodEventUpdate) 64 | } 65 | 66 | func (c *Controller) podDelete(obj interface{}) { 67 | 68 | if pod, ok := obj.(*v1.Pod); ok { 69 | c.preparePodEventForDispatch(pod, nil, cluster.PodEventDelete) 70 | } 71 | } 72 | 73 | func (c *Controller) preparePodEventForDispatch(curPod, prevPod *v1.Pod, event cluster.PodEventType) { 74 | podEvent := cluster.PodEvent{ 75 | PodName: types.NamespacedName(util.NameFromMeta(curPod.ObjectMeta)), 76 | CurPod: curPod, 77 | PrevPod: prevPod, 78 | EventType: event, 79 | ResourceVersion: curPod.ResourceVersion, 80 | } 81 | 82 | c.dispatchPodEvent(c.podClusterName(curPod), podEvent) 83 | } 84 | -------------------------------------------------------------------------------- /pkg/controller/types.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "time" 5 | 6 | "k8s.io/apimachinery/pkg/types" 7 | 8 | acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 9 | ) 10 | 11 | // EventType contains type of the events for the TPRs and Pods received from Kubernetes 12 | type EventType string 13 | 14 | // Possible values for the EventType 15 | const ( 16 | EventAdd EventType = "ADD" 17 | EventUpdate EventType = "UPDATE" 18 | EventDelete EventType = "DELETE" 19 | EventSync EventType = "SYNC" 20 | EventRepair EventType = "REPAIR" 21 | ) 22 | 23 | // ClusterEvent carries the payload of the Cluster TPR events. 24 | type ClusterEvent struct { 25 | EventTime time.Time 26 | UID types.UID 27 | EventType EventType 28 | OldSpec *acidv1.Postgresql 29 | NewSpec *acidv1.Postgresql 30 | WorkerID uint32 31 | } 32 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | // This package has the automatically generated clientset. 26 | package versioned 27 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | // This package has the automatically generated fake clientset. 26 | package fake 27 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package fake 26 | 27 | import ( 28 | acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 29 | zalandov1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" 30 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 | runtime "k8s.io/apimachinery/pkg/runtime" 32 | schema "k8s.io/apimachinery/pkg/runtime/schema" 33 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 34 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 35 | ) 36 | 37 | var scheme = runtime.NewScheme() 38 | var codecs = serializer.NewCodecFactory(scheme) 39 | 40 | var localSchemeBuilder = runtime.SchemeBuilder{ 41 | acidv1.AddToScheme, 42 | zalandov1.AddToScheme, 43 | } 44 | 45 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 46 | // of clientsets, like in: 47 | // 48 | // import ( 49 | // "k8s.io/client-go/kubernetes" 50 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 51 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 52 | // ) 53 | // 54 | // kclientset, _ := kubernetes.NewForConfig(c) 55 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 56 | // 57 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 58 | // correctly. 59 | var AddToScheme = localSchemeBuilder.AddToScheme 60 | 61 | func init() { 62 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 63 | utilruntime.Must(AddToScheme(scheme)) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | // This package contains the scheme of the automatically generated clientset. 26 | package scheme 27 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package scheme 26 | 27 | import ( 28 | acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 29 | zalandov1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" 30 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 31 | runtime "k8s.io/apimachinery/pkg/runtime" 32 | schema "k8s.io/apimachinery/pkg/runtime/schema" 33 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 34 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 35 | ) 36 | 37 | var Scheme = runtime.NewScheme() 38 | var Codecs = serializer.NewCodecFactory(Scheme) 39 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 40 | var localSchemeBuilder = runtime.SchemeBuilder{ 41 | acidv1.AddToScheme, 42 | zalandov1.AddToScheme, 43 | } 44 | 45 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 46 | // of clientsets, like in: 47 | // 48 | // import ( 49 | // "k8s.io/client-go/kubernetes" 50 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 51 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 52 | // ) 53 | // 54 | // kclientset, _ := kubernetes.NewForConfig(c) 55 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 56 | // 57 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 58 | // correctly. 59 | var AddToScheme = localSchemeBuilder.AddToScheme 60 | 61 | func init() { 62 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 63 | utilruntime.Must(AddToScheme(Scheme)) 64 | } 65 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | // This package has the automatically generated typed clients. 26 | package v1 27 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | // Package fake has the automatically generated clients. 26 | package fake 27 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_acid.zalan.do_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package fake 26 | 27 | import ( 28 | v1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" 29 | rest "k8s.io/client-go/rest" 30 | testing "k8s.io/client-go/testing" 31 | ) 32 | 33 | type FakeAcidV1 struct { 34 | *testing.Fake 35 | } 36 | 37 | func (c *FakeAcidV1) OperatorConfigurations(namespace string) v1.OperatorConfigurationInterface { 38 | return &FakeOperatorConfigurations{c, namespace} 39 | } 40 | 41 | func (c *FakeAcidV1) PostgresTeams(namespace string) v1.PostgresTeamInterface { 42 | return &FakePostgresTeams{c, namespace} 43 | } 44 | 45 | func (c *FakeAcidV1) Postgresqls(namespace string) v1.PostgresqlInterface { 46 | return &FakePostgresqls{c, namespace} 47 | } 48 | 49 | // RESTClient returns a RESTClient that is used to communicate 50 | // with API server by this client implementation. 51 | func (c *FakeAcidV1) RESTClient() rest.Interface { 52 | var ret *rest.RESTClient 53 | return ret 54 | } 55 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/fake/fake_operatorconfiguration.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package fake 26 | 27 | import ( 28 | "context" 29 | 30 | acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 31 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 | schema "k8s.io/apimachinery/pkg/runtime/schema" 33 | testing "k8s.io/client-go/testing" 34 | ) 35 | 36 | // FakeOperatorConfigurations implements OperatorConfigurationInterface 37 | type FakeOperatorConfigurations struct { 38 | Fake *FakeAcidV1 39 | ns string 40 | } 41 | 42 | var operatorconfigurationsResource = schema.GroupVersionResource{Group: "acid.zalan.do", Version: "v1", Resource: "operatorconfigurations"} 43 | 44 | var operatorconfigurationsKind = schema.GroupVersionKind{Group: "acid.zalan.do", Version: "v1", Kind: "OperatorConfiguration"} 45 | 46 | // Get takes name of the operatorConfiguration, and returns the corresponding operatorConfiguration object, and an error if there is any. 47 | func (c *FakeOperatorConfigurations) Get(ctx context.Context, name string, options v1.GetOptions) (result *acidzalandov1.OperatorConfiguration, err error) { 48 | obj, err := c.Fake. 49 | Invokes(testing.NewGetAction(operatorconfigurationsResource, c.ns, name), &acidzalandov1.OperatorConfiguration{}) 50 | 51 | if obj == nil { 52 | return nil, err 53 | } 54 | return obj.(*acidzalandov1.OperatorConfiguration), err 55 | } 56 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package v1 26 | 27 | type OperatorConfigurationExpansion interface{} 28 | 29 | type PostgresTeamExpansion interface{} 30 | 31 | type PostgresqlExpansion interface{} 32 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/acid.zalan.do/v1/operatorconfiguration.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package v1 26 | 27 | import ( 28 | "context" 29 | 30 | acidzalandov1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 31 | scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme" 32 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 33 | rest "k8s.io/client-go/rest" 34 | ) 35 | 36 | // OperatorConfigurationsGetter has a method to return a OperatorConfigurationInterface. 37 | // A group's client should implement this interface. 38 | type OperatorConfigurationsGetter interface { 39 | OperatorConfigurations(namespace string) OperatorConfigurationInterface 40 | } 41 | 42 | // OperatorConfigurationInterface has methods to work with OperatorConfiguration resources. 43 | type OperatorConfigurationInterface interface { 44 | Get(ctx context.Context, name string, opts v1.GetOptions) (*acidzalandov1.OperatorConfiguration, error) 45 | OperatorConfigurationExpansion 46 | } 47 | 48 | // operatorConfigurations implements OperatorConfigurationInterface 49 | type operatorConfigurations struct { 50 | client rest.Interface 51 | ns string 52 | } 53 | 54 | // newOperatorConfigurations returns a OperatorConfigurations 55 | func newOperatorConfigurations(c *AcidV1Client, namespace string) *operatorConfigurations { 56 | return &operatorConfigurations{ 57 | client: c.RESTClient(), 58 | ns: namespace, 59 | } 60 | } 61 | 62 | // Get takes name of the operatorConfiguration, and returns the corresponding operatorConfiguration object, and an error if there is any. 63 | func (c *operatorConfigurations) Get(ctx context.Context, name string, options v1.GetOptions) (result *acidzalandov1.OperatorConfiguration, err error) { 64 | result = &acidzalandov1.OperatorConfiguration{} 65 | err = c.client.Get(). 66 | Namespace(c.ns). 67 | Resource("operatorconfigurations"). 68 | Name(name). 69 | VersionedParams(&options, scheme.ParameterCodec). 70 | Do(ctx). 71 | Into(result) 72 | return 73 | } 74 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/zalando.org/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | // This package has the automatically generated typed clients. 26 | package v1 27 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | // Package fake has the automatically generated clients. 26 | package fake 27 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/zalando.org/v1/fake/fake_zalando.org_client.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package fake 26 | 27 | import ( 28 | v1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/zalando.org/v1" 29 | rest "k8s.io/client-go/rest" 30 | testing "k8s.io/client-go/testing" 31 | ) 32 | 33 | type FakeZalandoV1 struct { 34 | *testing.Fake 35 | } 36 | 37 | func (c *FakeZalandoV1) FabricEventStreams(namespace string) v1.FabricEventStreamInterface { 38 | return &FakeFabricEventStreams{c, namespace} 39 | } 40 | 41 | // RESTClient returns a RESTClient that is used to communicate 42 | // with API server by this client implementation. 43 | func (c *FakeZalandoV1) RESTClient() rest.Interface { 44 | var ret *rest.RESTClient 45 | return ret 46 | } 47 | -------------------------------------------------------------------------------- /pkg/generated/clientset/versioned/typed/zalando.org/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by client-gen. DO NOT EDIT. 24 | 25 | package v1 26 | 27 | type FabricEventStreamExpansion interface{} 28 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/acid.zalan.do/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by informer-gen. DO NOT EDIT. 24 | 25 | package acid 26 | 27 | import ( 28 | v1 "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/acid.zalan.do/v1" 29 | internalinterfaces "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" 30 | ) 31 | 32 | // Interface provides access to each of this group's versions. 33 | type Interface interface { 34 | // V1 provides access to shared informers for resources in V1. 35 | V1() v1.Interface 36 | } 37 | 38 | type group struct { 39 | factory internalinterfaces.SharedInformerFactory 40 | namespace string 41 | tweakListOptions internalinterfaces.TweakListOptionsFunc 42 | } 43 | 44 | // New returns a new Interface. 45 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 46 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 47 | } 48 | 49 | // V1 returns a new v1.Interface. 50 | func (g *group) V1() v1.Interface { 51 | return v1.New(g.factory, g.namespace, g.tweakListOptions) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/acid.zalan.do/v1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by informer-gen. DO NOT EDIT. 24 | 25 | package v1 26 | 27 | import ( 28 | internalinterfaces "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" 29 | ) 30 | 31 | // Interface provides access to all the informers in this group version. 32 | type Interface interface { 33 | // PostgresTeams returns a PostgresTeamInformer. 34 | PostgresTeams() PostgresTeamInformer 35 | // Postgresqls returns a PostgresqlInformer. 36 | Postgresqls() PostgresqlInformer 37 | } 38 | 39 | type version struct { 40 | factory internalinterfaces.SharedInformerFactory 41 | namespace string 42 | tweakListOptions internalinterfaces.TweakListOptionsFunc 43 | } 44 | 45 | // New returns a new Interface. 46 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 47 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 48 | } 49 | 50 | // PostgresTeams returns a PostgresTeamInformer. 51 | func (v *version) PostgresTeams() PostgresTeamInformer { 52 | return &postgresTeamInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 53 | } 54 | 55 | // Postgresqls returns a PostgresqlInformer. 56 | func (v *version) Postgresqls() PostgresqlInformer { 57 | return &postgresqlInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 58 | } 59 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by informer-gen. DO NOT EDIT. 24 | 25 | package externalversions 26 | 27 | import ( 28 | "fmt" 29 | 30 | v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" 31 | zalandoorgv1 "github.com/zalando/postgres-operator/pkg/apis/zalando.org/v1" 32 | schema "k8s.io/apimachinery/pkg/runtime/schema" 33 | cache "k8s.io/client-go/tools/cache" 34 | ) 35 | 36 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 37 | // sharedInformers based on type 38 | type GenericInformer interface { 39 | Informer() cache.SharedIndexInformer 40 | Lister() cache.GenericLister 41 | } 42 | 43 | type genericInformer struct { 44 | informer cache.SharedIndexInformer 45 | resource schema.GroupResource 46 | } 47 | 48 | // Informer returns the SharedIndexInformer. 49 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 50 | return f.informer 51 | } 52 | 53 | // Lister returns the GenericLister. 54 | func (f *genericInformer) Lister() cache.GenericLister { 55 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 56 | } 57 | 58 | // ForResource gives generic access to a shared informer of the matching type 59 | // TODO extend this to unknown resources with a client pool 60 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 61 | switch resource { 62 | // Group=acid.zalan.do, Version=v1 63 | case v1.SchemeGroupVersion.WithResource("postgresteams"): 64 | return &genericInformer{resource: resource.GroupResource(), informer: f.Acid().V1().PostgresTeams().Informer()}, nil 65 | case v1.SchemeGroupVersion.WithResource("postgresqls"): 66 | return &genericInformer{resource: resource.GroupResource(), informer: f.Acid().V1().Postgresqls().Informer()}, nil 67 | 68 | // Group=zalando.org, Version=v1 69 | case zalandoorgv1.SchemeGroupVersion.WithResource("fabriceventstreams"): 70 | return &genericInformer{resource: resource.GroupResource(), informer: f.Zalando().V1().FabricEventStreams().Informer()}, nil 71 | 72 | } 73 | 74 | return nil, fmt.Errorf("no informer found for %v", resource) 75 | } 76 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by informer-gen. DO NOT EDIT. 24 | 25 | package internalinterfaces 26 | 27 | import ( 28 | time "time" 29 | 30 | versioned "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned" 31 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 32 | runtime "k8s.io/apimachinery/pkg/runtime" 33 | cache "k8s.io/client-go/tools/cache" 34 | ) 35 | 36 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 37 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 38 | 39 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 40 | type SharedInformerFactory interface { 41 | Start(stopCh <-chan struct{}) 42 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 43 | } 44 | 45 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 46 | type TweakListOptionsFunc func(*v1.ListOptions) 47 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/zalando.org/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by informer-gen. DO NOT EDIT. 24 | 25 | package zalando 26 | 27 | import ( 28 | internalinterfaces "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" 29 | v1 "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/zalando.org/v1" 30 | ) 31 | 32 | // Interface provides access to each of this group's versions. 33 | type Interface interface { 34 | // V1 provides access to shared informers for resources in V1. 35 | V1() v1.Interface 36 | } 37 | 38 | type group struct { 39 | factory internalinterfaces.SharedInformerFactory 40 | namespace string 41 | tweakListOptions internalinterfaces.TweakListOptionsFunc 42 | } 43 | 44 | // New returns a new Interface. 45 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 46 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 47 | } 48 | 49 | // V1 returns a new v1.Interface. 50 | func (g *group) V1() v1.Interface { 51 | return v1.New(g.factory, g.namespace, g.tweakListOptions) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/generated/informers/externalversions/zalando.org/v1/interface.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by informer-gen. DO NOT EDIT. 24 | 25 | package v1 26 | 27 | import ( 28 | internalinterfaces "github.com/zalando/postgres-operator/pkg/generated/informers/externalversions/internalinterfaces" 29 | ) 30 | 31 | // Interface provides access to all the informers in this group version. 32 | type Interface interface { 33 | // FabricEventStreams returns a FabricEventStreamInformer. 34 | FabricEventStreams() FabricEventStreamInformer 35 | } 36 | 37 | type version struct { 38 | factory internalinterfaces.SharedInformerFactory 39 | namespace string 40 | tweakListOptions internalinterfaces.TweakListOptionsFunc 41 | } 42 | 43 | // New returns a new Interface. 44 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 45 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 46 | } 47 | 48 | // FabricEventStreams returns a FabricEventStreamInformer. 49 | func (v *version) FabricEventStreams() FabricEventStreamInformer { 50 | return &fabricEventStreamInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} 51 | } 52 | -------------------------------------------------------------------------------- /pkg/generated/listers/acid.zalan.do/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by lister-gen. DO NOT EDIT. 24 | 25 | package v1 26 | 27 | // PostgresTeamListerExpansion allows custom methods to be added to 28 | // PostgresTeamLister. 29 | type PostgresTeamListerExpansion interface{} 30 | 31 | // PostgresTeamNamespaceListerExpansion allows custom methods to be added to 32 | // PostgresTeamNamespaceLister. 33 | type PostgresTeamNamespaceListerExpansion interface{} 34 | 35 | // PostgresqlListerExpansion allows custom methods to be added to 36 | // PostgresqlLister. 37 | type PostgresqlListerExpansion interface{} 38 | 39 | // PostgresqlNamespaceListerExpansion allows custom methods to be added to 40 | // PostgresqlNamespaceLister. 41 | type PostgresqlNamespaceListerExpansion interface{} 42 | -------------------------------------------------------------------------------- /pkg/generated/listers/zalando.org/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2025 Compose, Zalando SE 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | */ 22 | 23 | // Code generated by lister-gen. DO NOT EDIT. 24 | 25 | package v1 26 | 27 | // FabricEventStreamListerExpansion allows custom methods to be added to 28 | // FabricEventStreamLister. 29 | type FabricEventStreamListerExpansion interface{} 30 | 31 | // FabricEventStreamNamespaceListerExpansion allows custom methods to be added to 32 | // FabricEventStreamNamespaceLister. 33 | type FabricEventStreamNamespaceListerExpansion interface{} 34 | -------------------------------------------------------------------------------- /pkg/spec/types_test.go: -------------------------------------------------------------------------------- 1 | package spec 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | const ( 9 | mockOperatorNamespace = "acid" 10 | ) 11 | 12 | var nnTests = []struct { 13 | s string 14 | expected NamespacedName 15 | expectedMarshal []byte 16 | }{ 17 | {`acid/cluster`, NamespacedName{Namespace: mockOperatorNamespace, Name: "cluster"}, []byte(`"acid/cluster"`)}, 18 | {`/name`, NamespacedName{Namespace: mockOperatorNamespace, Name: "name"}, []byte(`"acid/name"`)}, 19 | {`test`, NamespacedName{Namespace: mockOperatorNamespace, Name: "test"}, []byte(`"acid/test"`)}, 20 | } 21 | 22 | var nnErr = []string{"test/", "/", "", "//"} 23 | 24 | func TestNamespacedNameDecode(t *testing.T) { 25 | 26 | for _, tt := range nnTests { 27 | var actual NamespacedName 28 | err := actual.DecodeWorker(tt.s, mockOperatorNamespace) 29 | if err != nil { 30 | t.Errorf("decode error: %v", err) 31 | } 32 | if actual != tt.expected { 33 | t.Errorf("expected: %v, got %#v", tt.expected, actual) 34 | } 35 | } 36 | 37 | } 38 | 39 | func TestNamespacedNameMarshal(t *testing.T) { 40 | for _, tt := range nnTests { 41 | var actual NamespacedName 42 | 43 | m, err := actual.MarshalJSON() 44 | if err != nil { 45 | t.Errorf("marshal error: %v", err) 46 | } 47 | if bytes.Equal(m, tt.expectedMarshal) { 48 | t.Errorf("expected marshal: %v, got %#v", tt.expected, actual) 49 | } 50 | } 51 | } 52 | 53 | func TestNamespacedNameError(t *testing.T) { 54 | for _, tt := range nnErr { 55 | var actual NamespacedName 56 | err := actual.DecodeWorker(tt, mockOperatorNamespace) 57 | if err == nil { 58 | t.Errorf("error expected for %q, got: %#v", tt, actual) 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /pkg/util/config/config_test.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | var getMapPairsFromStringTest = []struct { 10 | in string 11 | expected []string 12 | err error 13 | }{ 14 | {"log_statement:all, work_mem:'4GB'", []string{"log_statement:all", "work_mem:'4GB'"}, nil}, 15 | {`log_statement:none, search_path:'"$user", public'`, []string{"log_statement:none", `search_path:'"$user", public'`}, nil}, 16 | {`search_path:'"$user"`, nil, fmt.Errorf("unmatched quote starting at position 13")}, 17 | {"", []string{""}, nil}, 18 | {",,log_statement:all ,", []string{"", "", "log_statement:all", ""}, nil}, 19 | } 20 | 21 | func TestGetMapPairsFromString(t *testing.T) { 22 | for _, tt := range getMapPairsFromStringTest { 23 | got, err := getMapPairsFromString(tt.in) 24 | if err != tt.err && ((err == nil || tt.err == nil) || (err.Error() != tt.err.Error())) { 25 | t.Errorf("TestGetMapPairsFromString with %s: expected error: %#v, got %#v", tt.in, tt.err, err) 26 | } 27 | if !reflect.DeepEqual(got, tt.expected) { 28 | t.Errorf("TestGetMapPairsFromString with %s: expected %#v, got %#v", tt.in, tt.expected, got) 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/util/constants/annotations.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | // Names and values in Kubernetes annotation for services, statefulsets and volumes 4 | const ( 5 | ZalandoDNSNameAnnotation = "external-dns.alpha.kubernetes.io/hostname" 6 | ElbTimeoutAnnotationName = "service.beta.kubernetes.io/aws-load-balancer-connection-idle-timeout" 7 | ElbTimeoutAnnotationValue = "3600" 8 | KubeIAmAnnotation = "iam.amazonaws.com/role" 9 | VolumeStorateProvisionerAnnotation = "pv.kubernetes.io/provisioned-by" 10 | PostgresqlControllerAnnotationKey = "acid.zalan.do/controller" 11 | ) 12 | -------------------------------------------------------------------------------- /pkg/util/constants/aws.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import "time" 4 | 5 | // AWS specific constants used by other modules 6 | const ( 7 | // EBS related constants 8 | EBSVolumeIDStart = "/vol-" 9 | EBSProvisioner = "kubernetes.io/aws-ebs" 10 | EBSDriver = "ebs.csi.aws.com" 11 | //https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_VolumeModification.html 12 | EBSVolumeStateModifying = "modifying" 13 | EBSVolumeStateOptimizing = "optimizing" 14 | EBSVolumeStateFailed = "failed" 15 | EBSVolumeStateCompleted = "completed" 16 | EBSVolumeResizeWaitInterval = 2 * time.Second 17 | EBSVolumeResizeWaitTimeout = 30 * time.Second 18 | ) 19 | -------------------------------------------------------------------------------- /pkg/util/constants/kubernetes.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import "time" 4 | 5 | // General kubernetes-related constants 6 | const ( 7 | PostgresContainerName = "postgres" 8 | K8sAPIPath = "/apis" 9 | 10 | QueueResyncPeriodPod = 5 * time.Minute 11 | QueueResyncPeriodTPR = 5 * time.Minute 12 | QueueResyncPeriodNode = 5 * time.Minute 13 | ) 14 | -------------------------------------------------------------------------------- /pkg/util/constants/pooler.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | // Connection pooler specific constants 4 | const ( 5 | ConnectionPoolerResourceSuffix = "pooler" 6 | ConnectionPoolerUserName = "pooler" 7 | ConnectionPoolerSchemaName = "pooler" 8 | ConnectionPoolerDefaultType = "pgbouncer" 9 | ConnectionPoolerDefaultMode = "transaction" 10 | ConnectionPoolerDefaultCpuRequest = "500m" 11 | ConnectionPoolerDefaultCpuLimit = "1" 12 | ConnectionPoolerDefaultMemoryRequest = "100Mi" 13 | ConnectionPoolerDefaultMemoryLimit = "100Mi" 14 | 15 | ConnectionPoolerContainer = 0 16 | ConnectionPoolerMaxDBConnections = 60 17 | ConnectionPoolerMaxClientConnections = 10000 18 | ConnectionPoolerMinInstances = 1 19 | ) 20 | -------------------------------------------------------------------------------- /pkg/util/constants/postgresql.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | import "time" 4 | 5 | // PostgreSQL specific constants 6 | const ( 7 | DataVolumeName = "pgdata" 8 | PostgresDataMount = "/home/postgres/pgdata" 9 | PostgresDataPath = PostgresDataMount + "/pgroot" 10 | 11 | PatroniPGParametersParameterName = "parameters" 12 | 13 | PostgresConnectRetryTimeout = 2 * time.Minute 14 | PostgresConnectTimeout = 15 * time.Second 15 | 16 | ShmVolumeName = "dshm" 17 | ShmVolumePath = "/dev/shm" 18 | 19 | RunVolumeName = "postgresql-run" 20 | RunVolumePath = "/var/run/postgresql" 21 | ) 22 | -------------------------------------------------------------------------------- /pkg/util/constants/roles.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | // Roles specific constants 4 | const ( 5 | PasswordLength = 64 6 | SuperuserKeyName = "superuser" 7 | ReplicationUserKeyName = "replication" 8 | ConnectionPoolerUserKeyName = "pooler" 9 | EventStreamUserKeyName = "streamer" 10 | RoleFlagSuperuser = "SUPERUSER" 11 | RoleFlagInherit = "INHERIT" 12 | RoleFlagLogin = "LOGIN" 13 | RoleFlagNoLogin = "NOLOGIN" 14 | RoleFlagCreateRole = "CREATEROLE" 15 | RoleFlagCreateDB = "CREATEDB" 16 | RoleFlagReplication = "REPLICATION" 17 | RoleFlagByPassRLS = "BYPASSRLS" 18 | OwnerRoleNameSuffix = "_owner" 19 | ReaderRoleNameSuffix = "_reader" 20 | WriterRoleNameSuffix = "_writer" 21 | UserRoleNameSuffix = "_user" 22 | DefaultSearchPath = "\"$user\"" 23 | RotationUserDateFormat = "060102" 24 | ) 25 | -------------------------------------------------------------------------------- /pkg/util/constants/streams.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | // PostgreSQL specific constants 4 | const ( 5 | EventStreamCRDApiVersion = "zalando.org/v1" 6 | EventStreamCRDKind = "FabricEventStream" 7 | EventStreamCRDName = "fabriceventstreams.zalando.org" 8 | EventStreamSourcePGType = "PostgresLogicalReplication" 9 | EventStreamSourceSlotPrefix = "fes" 10 | EventStreamSourcePluginType = "pgoutput" 11 | EventStreamSourceAuthType = "DatabaseAuthenticationSecret" 12 | EventStreamFlowPgGenericType = "PostgresWalToGenericNakadiEvent" 13 | EventStreamSinkNakadiType = "Nakadi" 14 | EventStreamRecoveryDLQType = "DeadLetter" 15 | EventStreamRecoveryIgnoreType = "Ignore" 16 | EventStreamRecoveryNoneType = "None" 17 | EventStreamRecoverySuffix = "dead-letter-queue" 18 | EventStreamCpuAnnotationKey = "fes.zalando.org/FES_CPU" 19 | EventStreamMemoryAnnotationKey = "fes.zalando.org/FES_MEMORY" 20 | ) 21 | -------------------------------------------------------------------------------- /pkg/util/constants/units.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | // Measurement-unit definitions 4 | const ( 5 | Gigabyte = 1073741824 6 | ) 7 | -------------------------------------------------------------------------------- /pkg/util/filesystems/ext234.go: -------------------------------------------------------------------------------- 1 | package filesystems 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | var ( 10 | ext2fsSuccessRegexp = regexp.MustCompile(`The filesystem on [/a-z0-9]+ is now \d+ \(\d+\w+\) blocks long.`) 11 | ) 12 | 13 | const ( 14 | ext2 = "ext2" 15 | ext3 = "ext3" 16 | ext4 = "ext4" 17 | resize2fs = "resize2fs" 18 | ) 19 | 20 | //Ext234Resize implements the FilesystemResizer interface for the ext4/3/2fs. 21 | type Ext234Resize struct { 22 | } 23 | 24 | // CanResizeFilesystem checks whether Ext234Resize can resize this filesystem. 25 | func (c *Ext234Resize) CanResizeFilesystem(fstype string) bool { 26 | return fstype == ext2 || fstype == ext3 || fstype == ext4 27 | } 28 | 29 | // ResizeFilesystem calls resize2fs to resize the filesystem if necessary. 30 | func (c *Ext234Resize) ResizeFilesystem(deviceName string, commandExecutor func(cmd string) (out string, err error)) error { 31 | command := fmt.Sprintf("%s %s 2>&1", resize2fs, deviceName) 32 | out, err := commandExecutor(command) 33 | if err != nil { 34 | return err 35 | } 36 | if strings.Contains(out, "Nothing to do") || 37 | (strings.Contains(out, "on-line resizing required") && ext2fsSuccessRegexp.MatchString(out)) { 38 | return nil 39 | } 40 | return fmt.Errorf("unrecognized output: %q, assuming error", out) 41 | } 42 | -------------------------------------------------------------------------------- /pkg/util/filesystems/filesystems.go: -------------------------------------------------------------------------------- 1 | package filesystems 2 | 3 | // FilesystemResizer has methods to work with resizing of a filesystem 4 | type FilesystemResizer interface { 5 | CanResizeFilesystem(fstype string) bool 6 | ResizeFilesystem(deviceName string, commandExecutor func(string) (out string, err error)) error 7 | } 8 | -------------------------------------------------------------------------------- /pkg/util/httpclient/httpclient.go: -------------------------------------------------------------------------------- 1 | package httpclient 2 | 3 | //go:generate mockgen -package mocks -destination=../../../mocks/$GOFILE -source=$GOFILE -build_flags=-mod=vendor 4 | 5 | import "net/http" 6 | 7 | // HTTPClient interface 8 | type HTTPClient interface { 9 | Do(req *http.Request) (*http.Response, error) 10 | Get(url string) (resp *http.Response, err error) 11 | } 12 | -------------------------------------------------------------------------------- /pkg/util/retryutil/retry_util.go: -------------------------------------------------------------------------------- 1 | package retryutil 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // RetryTicker is a wrapper aroung time.Tick, 9 | // that allows to mock its implementation 10 | type RetryTicker interface { 11 | Stop() 12 | Tick() 13 | } 14 | 15 | // Ticker is a real implementation of RetryTicker interface 16 | type Ticker struct { 17 | ticker *time.Ticker 18 | } 19 | 20 | // Stop is a convenience wrapper around ticker.Stop 21 | func (t *Ticker) Stop() { t.ticker.Stop() } 22 | 23 | // Tick is a convenience wrapper around ticker.C 24 | func (t *Ticker) Tick() { <-t.ticker.C } 25 | 26 | // Retry is a wrapper around RetryWorker that provides a real RetryTicker 27 | func Retry(interval time.Duration, timeout time.Duration, f func() (bool, error)) error { 28 | //TODO: make the retry exponential 29 | if timeout < interval { 30 | return fmt.Errorf("timeout(%s) should be greater than interval(%v)", timeout, interval) 31 | } 32 | tick := &Ticker{time.NewTicker(interval)} 33 | return RetryWorker(interval, timeout, tick, f) 34 | } 35 | 36 | // RetryWorker calls ConditionFunc until either: 37 | // * it returns boolean true 38 | // * a timeout expires 39 | // * an error occurs 40 | func RetryWorker( 41 | interval time.Duration, 42 | timeout time.Duration, 43 | tick RetryTicker, 44 | f func() (bool, error)) error { 45 | 46 | maxRetries := int(timeout / interval) 47 | defer tick.Stop() 48 | 49 | for i := 0; ; i++ { 50 | ok, err := f() 51 | if err != nil { 52 | return err 53 | } 54 | if ok { 55 | return nil 56 | } 57 | if i+1 == maxRetries { 58 | break 59 | } 60 | tick.Tick() 61 | } 62 | return fmt.Errorf("still failing after %d retries", maxRetries) 63 | } 64 | -------------------------------------------------------------------------------- /pkg/util/retryutil/retry_util_test.go: -------------------------------------------------------------------------------- 1 | package retryutil 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | ) 7 | 8 | type mockTicker struct { 9 | test *testing.T 10 | counter int 11 | } 12 | 13 | func (t *mockTicker) Stop() {} 14 | 15 | func (t *mockTicker) Tick() { 16 | t.counter++ 17 | } 18 | 19 | func TestRetryWorkerSuccess(t *testing.T) { 20 | tick := &mockTicker{t, 0} 21 | result := RetryWorker(10, 20, tick, func() (bool, error) { 22 | return true, nil 23 | }) 24 | 25 | if result != nil { 26 | t.Errorf("Wrong result, expected: %#v, got: %#v", nil, result) 27 | } 28 | 29 | if tick.counter != 0 { 30 | t.Errorf("Ticker was started once, but it shouldn't be") 31 | } 32 | } 33 | 34 | func TestRetryWorkerOneFalse(t *testing.T) { 35 | var counter = 0 36 | 37 | tick := &mockTicker{t, 0} 38 | result := RetryWorker(1, 3, tick, func() (bool, error) { 39 | counter++ 40 | return counter > 1, nil 41 | }) 42 | 43 | if result != nil { 44 | t.Errorf("Wrong result, expected: %#v, got: %#v", nil, result) 45 | } 46 | 47 | if tick.counter != 1 { 48 | t.Errorf("Ticker was started %#v, but supposed to be just once", tick.counter) 49 | } 50 | } 51 | 52 | func TestRetryWorkerError(t *testing.T) { 53 | fail := errors.New("Error") 54 | 55 | tick := &mockTicker{t, 0} 56 | result := RetryWorker(1, 3, tick, func() (bool, error) { 57 | return false, fail 58 | }) 59 | 60 | if result != fail { 61 | t.Errorf("Wrong result, expected: %#v, got: %#v", fail, result) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /pkg/util/ringlog/ringlog.go: -------------------------------------------------------------------------------- 1 | package ringlog 2 | 3 | import ( 4 | "container/list" 5 | "sync" 6 | ) 7 | 8 | // RingLogger describes ring logger methods 9 | type RingLogger interface { 10 | Insert(interface{}) 11 | Walk() []interface{} 12 | } 13 | 14 | // RingLog is a capped logger with fixed size 15 | type RingLog struct { 16 | sync.RWMutex 17 | size int 18 | list *list.List 19 | } 20 | 21 | // New creates new Ring logger 22 | func New(size int) *RingLog { 23 | r := RingLog{ 24 | list: list.New(), 25 | size: size, 26 | } 27 | 28 | return &r 29 | } 30 | 31 | // Insert inserts new entry into the ring logger 32 | func (r *RingLog) Insert(obj interface{}) { 33 | r.Lock() 34 | defer r.Unlock() 35 | 36 | r.list.PushBack(obj) 37 | if r.list.Len() > r.size { 38 | r.list.Remove(r.list.Front()) 39 | } 40 | } 41 | 42 | // Walk dumps all the entries from the Ring logger 43 | func (r *RingLog) Walk() []interface{} { 44 | res := make([]interface{}, 0) 45 | 46 | r.RLock() 47 | defer r.RUnlock() 48 | 49 | st := r.list.Front() 50 | for i := 0; i < r.size; i++ { 51 | if st == nil { 52 | return res 53 | } 54 | res = append(res, st.Value) 55 | st = st.Next() 56 | } 57 | 58 | return res 59 | } 60 | -------------------------------------------------------------------------------- /pkg/util/volumes/volumes.go: -------------------------------------------------------------------------------- 1 | package volumes 2 | 3 | //go:generate mockgen -package mocks -destination=../../../mocks/$GOFILE -source=$GOFILE -build_flags=-mod=vendor 4 | 5 | import v1 "k8s.io/api/core/v1" 6 | 7 | // VolumeProperties ... 8 | type VolumeProperties struct { 9 | VolumeID string 10 | VolumeType string 11 | Size int64 12 | Iops int64 13 | Throughput int64 14 | } 15 | 16 | // VolumeResizer defines the set of methods used to implememnt provider-specific resizing of persistent volumes. 17 | type VolumeResizer interface { 18 | ConnectToProvider() error 19 | IsConnectedToProvider() bool 20 | VolumeBelongsToProvider(pv *v1.PersistentVolume) bool 21 | GetProviderVolumeID(pv *v1.PersistentVolume) (string, error) 22 | ExtractVolumeID(volumeID string) (string, error) 23 | ResizeVolume(providerVolumeID string, newSize int64) error 24 | ModifyVolume(providerVolumeID string, newType *string, newSize *int64, iops *int64, throughput *int64) error 25 | DisconnectFromProvider() error 26 | DescribeVolumes(providerVolumesID []string) ([]VolumeProperties, error) 27 | } 28 | -------------------------------------------------------------------------------- /pkg/util/volumes/volumes_test.go: -------------------------------------------------------------------------------- 1 | package volumes 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestExtractVolumeID(t *testing.T) { 9 | var tests = []struct { 10 | input string 11 | expectedResult string 12 | expectedErr error 13 | }{ 14 | { 15 | input: "aws://eu-central-1c/vol-01234a5b6c78df9gh", 16 | expectedResult: "vol-01234a5b6c78df9gh", 17 | expectedErr: nil, 18 | }, 19 | { 20 | input: "vol-0g9fd87c6b5a43210", 21 | expectedResult: "vol-0g9fd87c6b5a43210", 22 | expectedErr: nil, 23 | }, 24 | { 25 | input: "aws://eu-central-1c/01234a5b6c78df9g0", 26 | expectedResult: "", 27 | expectedErr: fmt.Errorf("malformed EBS volume id %q", "aws://eu-central-1c/01234a5b6c78df9g0"), 28 | }, 29 | { 30 | input: "hg9fd87c6b5a43210", 31 | expectedResult: "", 32 | expectedErr: fmt.Errorf("malformed EBS volume id %q", "hg9fd87c6b5a43210"), 33 | }, 34 | } 35 | 36 | resizer := EBSVolumeResizer{} 37 | 38 | for _, tt := range tests { 39 | volumeId, err := resizer.ExtractVolumeID(tt.input) 40 | if volumeId != tt.expectedResult { 41 | t.Errorf("%s expected: %s, got %s", t.Name(), tt.expectedResult, volumeId) 42 | } 43 | if err != tt.expectedErr { 44 | if tt.expectedErr != nil && err.Error() != tt.expectedErr.Error() { 45 | t.Errorf("%s unexpected error: got %v", t.Name(), err) 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ui/.dockerignore: -------------------------------------------------------------------------------- 1 | *# 2 | *.pyc 3 | *~ 4 | .*.sw? 5 | .git 6 | __pycache__ 7 | 8 | .npm/ 9 | 10 | app/node_modules 11 | operator_ui/static/build/*.hot-update.js 12 | operator_ui/static/build/*.hot-update.json 13 | -------------------------------------------------------------------------------- /ui/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=registry.opensource.zalan.do/library/python-3.11-slim:latest 2 | ARG NODE_IMAGE=node:lts-alpine 3 | 4 | FROM $NODE_IMAGE AS build 5 | 6 | COPY . /workdir 7 | WORKDIR /workdir/app 8 | 9 | RUN npm install \ 10 | && npm run build 11 | 12 | FROM $BASE_IMAGE 13 | LABEL maintainer="Team ACID @ Zalando " 14 | 15 | EXPOSE 8081 16 | WORKDIR /app 17 | 18 | RUN apt-get -qq -y update \ 19 | # https://www.psycopg.org/docs/install.html#psycopg-vs-psycopg-binary 20 | && apt-get -qq -y install --no-install-recommends g++ libpq-dev python3-dev python3-distutils \ 21 | && apt-get -qq -y clean \ 22 | && rm -rf /var/lib/apt/lists/* 23 | 24 | COPY requirements.txt . 25 | COPY start_server.sh . 26 | RUN pip install -r requirements.txt 27 | 28 | COPY operator_ui operator_ui/ 29 | COPY --from=build /workdir/operator_ui/static/build/ operator_ui/static/build/ 30 | 31 | ARG VERSION=dev 32 | RUN sed -i "s/__version__ = .*/__version__ = '${VERSION}'/" operator_ui/__init__.py 33 | 34 | CMD ["python", "-m", "operator_ui"] 35 | -------------------------------------------------------------------------------- /ui/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include operator_ui/static * 2 | recursive-include operator_ui/templates * 3 | include *.rst 4 | -------------------------------------------------------------------------------- /ui/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean test appjs docker push mock 2 | 3 | IMAGE ?= registry.opensource.zalan.do/acid/postgres-operator-ui 4 | VERSION ?= $(shell git describe --tags --always --dirty) 5 | TAG ?= $(VERSION) 6 | GITHEAD = $(shell git rev-parse --short HEAD) 7 | GITURL = $(shell git config --get remote.origin.url) 8 | GITSTATUS = $(shell git status --porcelain || echo 'no changes') 9 | TTYFLAGS = $(shell test -t 0 && echo '-it') 10 | 11 | ifdef CDP_PULL_REQUEST_NUMBER 12 | CDP_TAG := -${CDP_BUILD_VERSION} 13 | endif 14 | 15 | default: docker 16 | 17 | clean: 18 | rm -fr operator_ui/static/build 19 | 20 | test: 21 | tox 22 | 23 | appjs: 24 | docker run $(TTYFLAGS) -u $$(id -u) -v $$(pwd):/workdir -w /workdir/app node:lts-alpine npm install --cache /workdir/.npm 25 | docker run $(TTYFLAGS) -u $$(id -u) -v $$(pwd):/workdir -w /workdir/app node:lts-alpine npm run build --cache /workdir/.npm 26 | 27 | docker: appjs 28 | echo `(env)` 29 | echo "Tag ${TAG}" 30 | echo "Version ${VERSION}" 31 | echo "CDP tag ${CDP_TAG}" 32 | echo "git describe $(shell git describe --tags --always --dirty)" 33 | docker build --rm -t "$(IMAGE):$(TAG)$(CDP_TAG)" -f Dockerfile . 34 | 35 | push: 36 | docker push "$(IMAGE):$(TAG)$(CDP_TAG)" 37 | 38 | mock: 39 | docker run -it -p 8081:8081 "$(IMAGE):$(TAG)" --mock 40 | -------------------------------------------------------------------------------- /ui/app/.eslintignore: -------------------------------------------------------------------------------- 1 | src/vendor/*.js 2 | -------------------------------------------------------------------------------- /ui/app/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | parserOptions: 2 | sourceType: module 3 | env: 4 | browser: true 5 | node: true 6 | es6: true 7 | extends: 'eslint:recommended' 8 | rules: 9 | indent: 10 | - error 11 | - 4 12 | linebreak-style: 13 | - error 14 | - unix 15 | quotes: 16 | - error 17 | - single 18 | prefer-const: 19 | - error 20 | no-redeclare: 21 | - error 22 | no-unused-vars: 23 | - warn 24 | - argsIgnorePattern: "^_" 25 | semi: 26 | - error 27 | - never 28 | -------------------------------------------------------------------------------- /ui/app/README.rst: -------------------------------------------------------------------------------- 1 | This directory contains the EcmaScript frontend code of the PostgreSQL Operator UI and is only needed during build time. 2 | 3 | The JavaScript application bundle (webpack) will be generated to ``operator_ui/static/build/app*.js`` by running: 4 | 5 | .. code-block:: bash 6 | 7 | $ npm install 8 | $ npm run build 9 | 10 | Frontend development is supported by watching the source code and continuously recompiling the webpack: 11 | 12 | .. code-block:: bash 13 | 14 | $ npm start 15 | -------------------------------------------------------------------------------- /ui/app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgres-operator-ui", 3 | "version": "1.14.0", 4 | "description": "PostgreSQL Operator UI", 5 | "main": "src/app.js", 6 | "config": { 7 | "buildDir": "../operator_ui/static/build" 8 | }, 9 | "scripts": { 10 | "prestart": "npm install", 11 | "start": "NODE_ENV=development webpack --watch", 12 | "webpack": "webpack --config ./webpack.config.js", 13 | "build": "NODE_ENV=development npm run webpack", 14 | "prewebpack": "npm run clean", 15 | "lint": "eslint ./src/**/*.js", 16 | "clean": "rimraf $npm_package_config_buildDir && mkdir $npm_package_config_buildDir" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/zalando/postgres-operator.git" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/zalando/postgres-operator.git/issues" 26 | }, 27 | "homepage": "https://github.com/zalando/postgres-operator.git#readme", 28 | "dependencies": { 29 | "@babel/core": "^7.20.12", 30 | "@babel/polyfill": "^7.12.1", 31 | "@babel/runtime": "^7.20.13", 32 | "pixi.js": "^7.1.1" 33 | }, 34 | "devDependencies": { 35 | "@babel/plugin-transform-runtime": "^7.19.6", 36 | "@babel/preset-env": "^7.20.2", 37 | "babel-loader": "^8.2.5", 38 | "brfs": "^2.0.2", 39 | "dedent-js": "1.0.1", 40 | "eslint": "^8.32.0", 41 | "js-yaml": "4.1.0", 42 | "pug": "^3.0.2", 43 | "rimraf": "^4.1.2", 44 | "riot": "^3.13.2", 45 | "riot-hot-reload": "1.0.0", 46 | "riot-route": "^3.1.4", 47 | "riot-tag-loader": "2.1.0", 48 | "sort-by": "^1.2.0", 49 | "transform-loader": "^0.2.4", 50 | "webpack": "^4.46.0", 51 | "webpack-cli": "^4.10.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /ui/app/src/help-edit.tag.pug: -------------------------------------------------------------------------------- 1 | help-edit 2 | 3 | h2 Help 4 | 5 | .well 6 | 7 | h3(style='margin-top: 0') 8 | | Docs 9 | 10 | a(href="{ opts.config.docs_link }") 11 | | more... 12 | 13 | h3 Editing 14 | 15 | p. 16 | The text box shows you the properties that can currently edit. Verify that the preview shows a valid part of the spec before submitting. After a successful submit the changes may take some time to be applied. 17 | 18 | h3 Volume size 19 | 20 | p. 21 | You need to specify in format "123Gi". You can only increase the volume size. You can only increase volume size once very 6 hours, per AWS limitation. 22 | 23 | virtual( 24 | if='{ opts.config.static_network_whitelist && Object.keys(opts.config.static_network_whitelist).length > 0 }' 25 | ) 26 | h3 IP Ranges 27 | 28 | // Raw tags are required here, as otherwise either riotjs removes space it shouldn't, or pugjs adds space it shouldn't. And it has to be all in one line, as it has to be a pre tag (otherwise the riotjs compiler breaks the whitespace). 29 |

# { network }
- { range }
30 | -------------------------------------------------------------------------------- /ui/app/src/help-general.tag.pug: -------------------------------------------------------------------------------- 1 | help-general 2 | 3 | h2 Help 4 | 5 | .well 6 | 7 | h3(style='margin-top: 0') 8 | | Docs 9 | 10 | a(href="{ opts.config.docs_link }") 11 | | more... 12 | 13 | h3 Basics 14 | 15 | p. 16 | The Postgres Operator will use your definition to create a new 17 | PostgreSQL cluster for you. You can either copy the yaml definition 18 | to a repositiory or hit create cluster (not available in prod). 19 | -------------------------------------------------------------------------------- /ui/app/src/logs.tag.pug: -------------------------------------------------------------------------------- 1 | logs 2 | 3 | h1.page-header(if='{ cluster_path }') 4 | nav(aria-label="breadcrumb") 5 | ol.breadcrumb 6 | 7 | li.breadcrumb-item 8 | a(href='./#/list') 9 | | PostgreSQL clusters 10 | 11 | li.breadcrumb-item 12 | a(href='./#/status/{ cluster_path }') 13 | | { qname } 14 | 15 | li.breadcrumb-item 16 | a(href='./#/logs/{ cluster_path }') 17 | | Logs 18 | 19 | .sk-spinner-pulse(if='{ logs === undefined }') 20 | 21 | .container-fluid(if='{ logs === null }') 22 | p 23 | | Error loading logs. Please 24 | | 25 | a(onclick="window.location.reload(true)") try again 26 | | 27 | | or 28 | | 29 | a(href="./") start over 30 | | . 31 | 32 | .container-fluid(if='{ logs }') 33 | 34 | table.table.table-hover 35 | 36 | tr(each='{ logs }') 37 | 38 | td(each='{ [levels[Level]] }') 39 | span.label.label-font-size(class='label-{ color_class }') 40 | | { label } 41 | 42 | td(style='white-space: pre') 43 | | { Time } 44 | 45 | td(style='font-family: monospace') 46 | | { Message } 47 | 48 | script. 49 | 50 | this.levels = { 51 | "panic": { label: "Panic" , color_class: "danger" }, 52 | "fatal": { label: "Fatal" , color_class: "danger" }, 53 | "error": { label: "Error" , color_class: "danger" }, 54 | "warning": { label: "Warning", color_class: "warning" }, 55 | "info": { label: "Info" , color_class: "primary" }, 56 | "debug": { label: "Debug" , color_class: "warning" }, 57 | } 58 | 59 | this.logs = undefined 60 | 61 | this.on('mount', () => { 62 | if ( 63 | this.namespace !== this.opts.namespace 64 | || this.clustername !== this.opts.clustername 65 | ) { 66 | const namespace = this.namespace = this.opts.namespace 67 | const clustername = this.clustername = this.opts.clustername 68 | const qname = this.qname = namespace + '/' + clustername 69 | const cluster_path = this.cluster_path = ( 70 | encodeURI(namespace) 71 | + '/' + encodeURI(clustername) 72 | ) 73 | ;( 74 | jQuery 75 | .get(`./operator/clusters/${cluster_path}/logs`) 76 | .done(logs => this.logs = logs.reverse()) 77 | .fail(() => this.logs = null) 78 | .always(() => this.update()) 79 | ) 80 | } 81 | }) 82 | -------------------------------------------------------------------------------- /ui/manifests/ingress.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "networking.k8s.io/v1" 2 | kind: "Ingress" 3 | metadata: 4 | name: "postgres-operator-ui" 5 | namespace: "default" 6 | labels: 7 | application: "postgres-operator-ui" 8 | spec: 9 | # ingressClassName: "ingress-nginx" 10 | rules: 11 | - host: "ui.example.org" 12 | http: 13 | paths: 14 | - path: / 15 | pathType: Prefix 16 | backend: 17 | service: 18 | name: "postgres-operator-ui" 19 | port: 20 | number: 80 21 | -------------------------------------------------------------------------------- /ui/manifests/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | resources: 4 | - deployment.yaml 5 | - ingress.yaml 6 | - service.yaml 7 | - ui-service-account-rbac.yaml 8 | -------------------------------------------------------------------------------- /ui/manifests/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: "v1" 2 | kind: "Service" 3 | metadata: 4 | name: "postgres-operator-ui" 5 | namespace: "default" 6 | labels: 7 | application: "postgres-operator-ui" 8 | spec: 9 | type: "ClusterIP" 10 | selector: 11 | name: "postgres-operator-ui" 12 | ports: 13 | - port: 80 14 | protocol: "TCP" 15 | targetPort: 8081 16 | -------------------------------------------------------------------------------- /ui/manifests/ui-service-account-rbac.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: postgres-operator-ui 5 | namespace: default 6 | 7 | --- 8 | apiVersion: rbac.authorization.k8s.io/v1 9 | kind: ClusterRole 10 | metadata: 11 | name: postgres-operator-ui 12 | rules: 13 | - apiGroups: 14 | - acid.zalan.do 15 | resources: 16 | - postgresqls 17 | verbs: 18 | - create 19 | - delete 20 | - get 21 | - list 22 | - patch 23 | - update 24 | - apiGroups: 25 | - "" 26 | resources: 27 | - pods 28 | verbs: 29 | - get 30 | - list 31 | - watch 32 | - apiGroups: 33 | - "" 34 | resources: 35 | - services 36 | verbs: 37 | - get 38 | - list 39 | - apiGroups: 40 | - apps 41 | resources: 42 | - deployments 43 | - statefulsets 44 | verbs: 45 | - get 46 | - list 47 | - apiGroups: 48 | - "" 49 | resources: 50 | - namespaces 51 | verbs: 52 | - get 53 | - list 54 | --- 55 | apiVersion: rbac.authorization.k8s.io/v1 56 | kind: ClusterRoleBinding 57 | metadata: 58 | name: postgres-operator-ui 59 | roleRef: 60 | apiGroup: rbac.authorization.k8s.io 61 | kind: ClusterRole 62 | name: postgres-operator-ui 63 | subjects: 64 | - kind: ServiceAccount 65 | name: postgres-operator-ui 66 | namespace: default 67 | -------------------------------------------------------------------------------- /ui/operator_ui/__init__.py: -------------------------------------------------------------------------------- 1 | # This version is replaced during release process. 2 | __version__ = '2017.0.dev1' 3 | -------------------------------------------------------------------------------- /ui/operator_ui/__main__.py: -------------------------------------------------------------------------------- 1 | from .main import main 2 | 3 | main() 4 | -------------------------------------------------------------------------------- /ui/operator_ui/adapters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/ui/operator_ui/adapters/__init__.py -------------------------------------------------------------------------------- /ui/operator_ui/adapters/logger.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from logging.config import dictConfig 3 | 4 | dictConfig( 5 | { 6 | "version": 1, 7 | "disable_existing_loggers": True, 8 | "formatters": { 9 | "json": { 10 | "class": "pythonjsonlogger.jsonlogger.JsonFormatter", 11 | "format": "%(asctime)s %(levelname)s: %(message)s", 12 | } 13 | }, 14 | "handlers": { 15 | "stream_handler": { 16 | "class": "logging.StreamHandler", 17 | "formatter": "json", 18 | "stream": "ext://flask.logging.wsgi_errors_stream", 19 | } 20 | }, 21 | "root": { 22 | "level": "DEBUG", 23 | "handlers": ["stream_handler"] 24 | } 25 | } 26 | ) 27 | 28 | 29 | class Logger: 30 | def __init__(self): 31 | self.logger = logging.getLogger(__name__) 32 | 33 | def debug(self, msg: str, *args, **kwargs): 34 | self.logger.debug(msg, *args, **kwargs) 35 | 36 | def info(self, msg: str, *args, **kwargs): 37 | self.logger.info(msg, *args, **kwargs) 38 | 39 | def error(self, msg: str, *args, **kwargs): 40 | self.logger.error(msg, *args, **kwargs) 41 | 42 | def exception(self, msg: str, *args, **kwargs): 43 | self.logger.exception(msg, *args, **kwargs) 44 | 45 | 46 | logger = Logger() 47 | -------------------------------------------------------------------------------- /ui/operator_ui/backoff.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def expo(n: int, base=2, factor=1, max_value=None): 5 | """Exponential decay. 6 | 7 | Adapted from https://github.com/litl/backoff/blob/master/backoff.py (MIT License) 8 | 9 | Args: 10 | base: The mathematical base of the exponentiation operation 11 | factor: Factor to multiply the exponentation by. 12 | max_value: The maximum value to yield. Once the value in the 13 | true exponential sequence exceeds this, the value 14 | of max_value will forever after be yielded. 15 | """ 16 | a = factor * base ** n 17 | if max_value is None or a < max_value: 18 | return a 19 | else: 20 | return max_value 21 | 22 | 23 | def random_jitter(value, jitter=1): 24 | """Jitter the value a random number of milliseconds. 25 | 26 | Copied from https://github.com/litl/backoff/blob/master/backoff.py (MIT License) 27 | 28 | This adds up to 1 second of additional time to the original value. 29 | Prior to backoff version 1.2 this was the default jitter behavior. 30 | Args: 31 | value: The unadulterated backoff value. 32 | """ 33 | return value + random.uniform(0, jitter) 34 | 35 | 36 | def full_jitter(value): 37 | """Jitter the value across the full range (0 to value). 38 | 39 | Copied from https://github.com/litl/backoff/blob/master/backoff.py (MIT License) 40 | 41 | This corresponds to the "Full Jitter" algorithm specified in the 42 | AWS blog's post on the performance of various jitter algorithms. 43 | (http://www.awsarchitectureblog.com/2015/03/backoff.html) 44 | 45 | Args: 46 | value: The unadulterated backoff value. 47 | """ 48 | return random.uniform(0, value) 49 | -------------------------------------------------------------------------------- /ui/operator_ui/mock.py: -------------------------------------------------------------------------------- 1 | import time 2 | import json 3 | import request 4 | 5 | 6 | class MockCluster: 7 | 8 | def get_pods(self): 9 | return [{"name": "cluster-1-XFF", "role": "master", "ip": "localhost", "port": "8080"}, 10 | {"name": "cluster-1-XFE", "role": "replica", "ip": "localhost", "port": "8080"}, 11 | {"name": "cluster-1-XFS", "role": "replica", "ip": "localhost", "port": "8080"}, 12 | {"name": "cluster-2-SJE", "role": "master", "ip": "localhost", "port": "8080"}] 13 | -------------------------------------------------------------------------------- /ui/operator_ui/static/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zalando/postgres-operator/ccb52c094d5b3a0f7acda0c1bac332aa78e28e4a/ui/operator_ui/static/favicon-96x96.png -------------------------------------------------------------------------------- /ui/operator_ui/static/prism.css: -------------------------------------------------------------------------------- 1 | /* http://prismjs.com/download.html?themes=prism&languages=yaml */ 2 | /** 3 | * prism.js default theme for JavaScript, CSS and HTML 4 | * Based on dabblet (http://dabblet.com) 5 | * @author Lea Verou 6 | */ 7 | 8 | code[class*="language-"], 9 | pre[class*="language-"] { 10 | color: black; 11 | background: none; 12 | text-shadow: 0 1px white; 13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | word-wrap: normal; 19 | line-height: 1.5; 20 | 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | 25 | -webkit-hyphens: none; 26 | -moz-hyphens: none; 27 | -ms-hyphens: none; 28 | hyphens: none; 29 | } 30 | 31 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 32 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 33 | text-shadow: none; 34 | background: #b3d4fc; 35 | } 36 | 37 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 38 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 39 | text-shadow: none; 40 | background: #b3d4fc; 41 | } 42 | 43 | @media print { 44 | code[class*="language-"], 45 | pre[class*="language-"] { 46 | text-shadow: none; 47 | } 48 | } 49 | 50 | /* Code blocks */ 51 | pre[class*="language-"] { 52 | padding: 1em; 53 | margin: .5em 0; 54 | overflow: auto; 55 | } 56 | 57 | :not(pre) > code[class*="language-"], 58 | pre[class*="language-"] { 59 | background: #f5f2f0; 60 | } 61 | 62 | /* Inline code */ 63 | :not(pre) > code[class*="language-"] { 64 | padding: .1em; 65 | border-radius: .3em; 66 | white-space: normal; 67 | } 68 | 69 | .token.comment, 70 | .token.prolog, 71 | .token.doctype, 72 | .token.cdata { 73 | color: slategray; 74 | } 75 | 76 | .token.punctuation { 77 | color: #999; 78 | } 79 | 80 | .namespace { 81 | opacity: .7; 82 | } 83 | 84 | .token.property, 85 | .token.tag, 86 | .token.boolean, 87 | .token.number, 88 | .token.constant, 89 | .token.symbol, 90 | .token.deleted { 91 | color: #905; 92 | } 93 | 94 | .token.selector, 95 | .token.attr-name, 96 | .token.string, 97 | .token.char, 98 | .token.builtin, 99 | .token.inserted { 100 | color: #690; 101 | } 102 | 103 | .token.operator, 104 | .token.entity, 105 | .token.url, 106 | .language-css .token.string, 107 | .style .token.string { 108 | color: #a67f59; 109 | background: hsla(0, 0%, 100%, .5); 110 | } 111 | 112 | .token.atrule, 113 | .token.attr-value, 114 | .token.keyword { 115 | color: #07a; 116 | } 117 | 118 | .token.function { 119 | color: #DD4A68; 120 | } 121 | 122 | .token.regex, 123 | .token.important, 124 | .token.variable { 125 | color: #e90; 126 | } 127 | 128 | .token.important, 129 | .token.bold { 130 | font-weight: bold; 131 | } 132 | .token.italic { 133 | font-style: italic; 134 | } 135 | 136 | .token.entity { 137 | cursor: help; 138 | } 139 | 140 | -------------------------------------------------------------------------------- /ui/operator_ui/static/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | 5 | h1, h2, h3 { 6 | font-family: 'Open Sans', sans-serif; 7 | } 8 | 9 | .font-robot { 10 | font-family: 'Roboto 300', sans-serif; 11 | } 12 | 13 | input:invalid { 14 | color: red; 15 | font-weight: 600; 16 | } 17 | 18 | ul.ips { list-style-type: none; margin: 0; padding: 0; overflow-x: hidden; } 19 | ul.ips li { margin: 0; padding: 0; } 20 | ul.ips label { margin: 0; padding: 0; } 21 | 22 | .panel-heading.collapsible { 23 | cursor: pointer; 24 | } 25 | 26 | .timeline { 27 | cursor: pointer; 28 | } 29 | 30 | .panel-heading .collapsible:after { 31 | color: grey; 32 | content: "\e113"; 33 | float: right; 34 | font-family: 'Glyphicons Halflings'; 35 | transition: all 0.5s; 36 | } 37 | 38 | .panel-heading.collapsed .collapsible:after { 39 | transform: rotate(-180deg); 40 | } 41 | 42 | :not(form):invalid,select.owner:disabled { 43 | border: 1px solid red; 44 | box-shadow: 0 0 10px red; 45 | } 46 | 47 | .page-header { 48 | margin-top: 0px; 49 | } 50 | 51 | .page-header h1 { 52 | margin-top: 0px; 53 | } 54 | 55 | label { 56 | font-weight: normal; 57 | margin-top: 0; 58 | } 59 | 60 | .sk-spinner-pulse { 61 | background-color: darkblue; 62 | } 63 | 64 | td { 65 | vertical-align: middle !important; 66 | } 67 | 68 | .tooltip { 69 | position: relative; 70 | display: inline-block; 71 | opacity: 1; 72 | font-size: 14px; 73 | font-weight: bold; 74 | z-index: 0; 75 | } 76 | .tooltip:after { 77 | content: '?'; 78 | display: inline-block; 79 | font-family: sans-serif; 80 | font-weight: bold; 81 | text-align: center; 82 | width: 16px; 83 | height: 16px; 84 | font-size: 12px; 85 | line-height: 16px; 86 | border-radius: 12px; 87 | padding: 0px; 88 | color: white; 89 | background: black; 90 | border: 1px solid black; 91 | } 92 | .tooltip .tooltiptext { 93 | visibility: hidden; 94 | width: 250px; 95 | background-color: white; 96 | color: #000; 97 | text-align: justify; 98 | border-radius: 6px; 99 | padding: 10px 10px; 100 | position: absolute; 101 | bottom: 150%; 102 | left: 50%; 103 | margin-left: -120px; 104 | border: 1px solid black; 105 | font-weight: normal; 106 | } 107 | .tooltip .tooltiptext::after { 108 | content: ""; 109 | position: absolute; 110 | top: 100%; 111 | left: 50%; 112 | margin-left: -5px; 113 | border-width: 5px; 114 | border-style: solid; 115 | border-color: black transparent transparent transparent; 116 | } 117 | .tooltip:hover .tooltiptext { 118 | visibility: visible; 119 | } 120 | -------------------------------------------------------------------------------- /ui/operator_ui/update.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | 4 | import gevent 5 | import json_delta 6 | import requests.exceptions 7 | 8 | from .backoff import expo, random_jitter 9 | from .utils import get_short_error_message 10 | 11 | logger = logging.getLogger(__name__) 12 | 13 | -------------------------------------------------------------------------------- /ui/requirements.txt: -------------------------------------------------------------------------------- 1 | backoff==2.2.1 2 | boto3==1.34.110 3 | boto==2.49.0 4 | click==8.1.7 5 | Flask==3.0.3 6 | furl==2.1.3 7 | gevent==24.2.1 8 | jq==1.7.0 9 | json_delta>=2.0.2 10 | kubernetes==11.0.0 11 | python-json-logger==2.0.7 12 | requests==2.32.2 13 | stups-tokens>=1.1.19 14 | werkzeug==3.0.6 15 | -------------------------------------------------------------------------------- /ui/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from setuptools import find_packages, setup 4 | from setuptools.command.test import test as TestCommand 5 | 6 | from pathlib import Path 7 | 8 | 9 | def read_version(package): 10 | with (Path(package) / '__init__.py').open() as fd: 11 | for line in fd: 12 | if line.startswith('__version__ = '): 13 | return line.split()[-1].strip().strip("'") 14 | 15 | 16 | version = read_version('operator_ui') 17 | 18 | 19 | class PyTest(TestCommand): 20 | 21 | user_options = [('cov-html=', None, 'Generate junit html report')] 22 | 23 | def initialize_options(self): 24 | TestCommand.initialize_options(self) 25 | self.cov = None 26 | self.pytest_args = ['--cov', 'operator_ui', '--cov-report', 'term-missing', '-v'] 27 | self.cov_html = False 28 | 29 | def finalize_options(self): 30 | TestCommand.finalize_options(self) 31 | if self.cov_html: 32 | self.pytest_args.extend(['--cov-report', 'html']) 33 | self.pytest_args.extend(['tests']) 34 | 35 | def run_tests(self): 36 | import pytest 37 | 38 | errno = pytest.main(self.pytest_args) 39 | sys.exit(errno) 40 | 41 | 42 | def readme(): 43 | return open('README.rst', encoding='utf-8').read() 44 | 45 | 46 | tests_require = [ 47 | 'pytest', 48 | 'pytest-cov' 49 | ] 50 | 51 | setup( 52 | name='operator-ui', 53 | packages=find_packages(), 54 | version=version, 55 | description='PostgreSQL Kubernetes Operator UI', 56 | long_description=readme(), 57 | author='team-acid@zalando.de', 58 | url='https://github.com/postgres-operator', 59 | keywords='PostgreSQL Kubernetes Operator UI', 60 | license='MIT', 61 | tests_require=tests_require, 62 | extras_require={'tests': tests_require}, 63 | cmdclass={'test': PyTest}, 64 | test_suite='tests', 65 | classifiers=[ 66 | 'Development Status :: 3', 67 | 'Intended Audience :: Developers', 68 | 'Intended Audience :: System Administrators', 69 | 'License :: OSI Approved :: MIT', 70 | 'Operating System :: OS Independent', 71 | 'Programming Language :: Python', 72 | 'Programming Language :: Python :: 3.11', 73 | 'Topic :: System :: Clustering', 74 | 'Topic :: System :: Monitoring', 75 | ], 76 | include_package_data=True, # needed to include JavaScript (see MANIFEST.in) 77 | entry_points={'console_scripts': ['operator-ui = operator_ui.main:main']} 78 | ) 79 | -------------------------------------------------------------------------------- /ui/start_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | /usr/bin/python3 -m operator_ui 3 | -------------------------------------------------------------------------------- /ui/tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | envlist=py35,flake8,eslint 3 | 4 | [tox:travis] 5 | 3.5=py35,flake8,eslint 6 | 7 | [testenv] 8 | deps=pytest 9 | commands= 10 | pip install -r requirements.txt 11 | python setup.py test 12 | 13 | [testenv:flake8] 14 | deps=flake8 15 | commands=python setup.py flake8 16 | 17 | [testenv:eslint] 18 | whitelist_externals=eslint 19 | changedir=app 20 | commands=eslint src 21 | 22 | [flake8] 23 | max-line-length=160 24 | ignore=E402 25 | 26 | [pylama] 27 | ignore=E402 28 | --------------------------------------------------------------------------------