├── .dockerignore ├── .drone.yml ├── .gitattributes ├── .github └── FUNDING.yml ├── .gitignore ├── .golangci.yaml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── PROJECT ├── PULL_REQUEST_TEMPLATE.md ├── README.md ├── build ├── .drone.yml ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── bin │ └── po-diff.sh ├── images │ └── build │ │ ├── Dockerfile │ │ ├── Makefile │ │ ├── build-scripts │ │ └── install-packages.sh │ │ └── root │ │ ├── build │ │ ├── rsyncd.sh │ │ └── run.sh │ │ └── usr │ │ └── local │ │ └── bin │ │ ├── setup-credentials-helper.sh │ │ └── xvfb-chrome └── makelib │ ├── cache.mk │ ├── common.mk │ ├── gcp.mk │ ├── gettext.mk │ ├── git-publish.mk │ ├── golang.mk │ ├── helm.mk │ ├── image.mk │ ├── k8s-tools.mk │ ├── kubebuilder-v1.mk │ ├── kubebuilder-v2.mk │ ├── kubebuilder-v3.mk │ ├── nodejs.mk │ ├── php.mk │ ├── protobuf.mk │ ├── react.mk │ ├── utils.mk │ └── wordpress.mk ├── cmd ├── mysql-operator-sidecar │ └── main.go ├── mysql-operator │ └── main.go └── orc-helper │ └── main.go ├── config ├── certmanager │ ├── certificate.yaml │ ├── kustomization.yaml │ └── kustomizeconfig.yaml ├── crd │ ├── bases │ │ ├── mysql.presslabs.org_mysqlbackups.yaml │ │ ├── mysql.presslabs.org_mysqlclusters.yaml │ │ ├── mysql.presslabs.org_mysqldatabases.yaml │ │ └── mysql.presslabs.org_mysqlusers.yaml │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── patches │ │ ├── cainjection_in_mysqlbackups.yaml │ │ ├── cainjection_in_mysqlclusters.yaml │ │ ├── cainjection_in_mysqldatabases.yaml │ │ ├── cainjection_in_mysqlusers.yaml │ │ ├── webhook_in_mysqlbackups.yaml │ │ ├── webhook_in_mysqlclusters.yaml │ │ ├── webhook_in_mysqldatabases.yaml │ │ └── webhook_in_mysqlusers.yaml ├── default │ ├── kustomization.yaml │ ├── manager_auth_proxy_patch.yaml │ ├── manager_webhook_patch.yaml │ └── webhookcainjection_patch.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── mysqlbackup_editor_role.yaml │ ├── mysqlbackup_viewer_role.yaml │ ├── mysqlcluster_editor_role.yaml │ ├── mysqlcluster_viewer_role.yaml │ ├── mysqldatabase_editor_role.yaml │ ├── mysqldatabase_viewer_role.yaml │ ├── mysqluser_editor_role.yaml │ ├── mysqluser_viewer_role.yaml │ ├── role.yaml │ └── role_binding.yaml ├── samples │ ├── mysql_v1alpha1_mysqlbackup.yaml │ ├── mysql_v1alpha1_mysqlcluster.yaml │ ├── mysql_v1alpha1_mysqldatabase.yaml │ └── mysql_v1alpha1_mysqluser.yaml └── webhook │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── service.yaml ├── deploy └── charts │ ├── mysql-cluster │ ├── .helmignore │ ├── Chart.yaml │ ├── templates │ │ ├── _helpers.tpl │ │ ├── backup-secret.yaml │ │ ├── cluster.yaml │ │ └── secret.yaml │ └── values.yaml │ └── mysql-operator │ ├── .helmignore │ ├── Chart.yaml │ ├── README.md │ ├── crds │ ├── mysql.presslabs.org_mysqlbackups.yaml │ ├── mysql.presslabs.org_mysqlclusters.yaml │ ├── mysql.presslabs.org_mysqldatabases.yaml │ └── mysql.presslabs.org_mysqlusers.yaml │ ├── templates │ ├── NOTES.txt │ ├── _helpers.tpl │ ├── _orchestrator-helpers.tpl │ ├── clusterrole.yaml │ ├── clusterrolebinding.yaml │ ├── clustersservicemonitor.yaml │ ├── orchestrator-config.yaml │ ├── orchestrator-ingress.yaml │ ├── orchestrator-raft-service.yaml │ ├── orchestrator-secret.yaml │ ├── pdb.yaml │ ├── service.yaml │ ├── serviceaccount.yaml │ ├── servicemonitor.yaml │ └── statefulset.yaml │ └── values.yaml ├── examples ├── example-backup-secret.yaml ├── example-backup.yaml ├── example-cluster-init.yaml ├── example-cluster-secret.yaml ├── example-cluster.yaml ├── example-database.yaml └── example-user.yaml ├── go.mod ├── go.sum ├── hack ├── 02x-crds.yaml ├── boilerplate.go.txt ├── chart-metadata.yaml ├── development │ ├── Dockerfile.operator │ ├── Dockerfile.orchestrator │ ├── Dockerfile.sidecar │ ├── dev-values.yaml │ ├── related-go-files.sh │ └── stress_test.py ├── generate_chart.sh ├── license-check └── upgrades-tests │ ├── test_upgrade_v0.2.0.sh │ └── test_upgrade_v0.3.0.sh ├── images ├── mysql-operator-orchestrator │ ├── Dockerfile │ ├── Makefile │ └── rootfs │ │ └── usr │ │ └── local │ │ └── share │ │ └── orchestrator │ │ └── templates │ │ ├── orc-topology.cnf │ │ └── orchestrator.conf.json ├── mysql-operator-sidecar-5.7 │ ├── Dockerfile │ ├── Makefile │ └── rootfs │ │ ├── etc │ │ ├── apt │ │ │ └── trusted.gpg.d │ │ │ │ └── percona.gpg │ │ └── gpg-keys │ │ │ └── rclone.gpg │ │ └── usr │ │ └── local │ │ └── bin │ │ └── docker-entrypoint.sh ├── mysql-operator-sidecar-8.0 │ └── Makefile └── mysql-operator │ ├── Dockerfile │ └── Makefile ├── pkg ├── apis │ ├── addtoscheme_mysql_v1alpha1.go │ ├── apis.go │ └── mysql │ │ ├── group.go │ │ └── v1alpha1 │ │ ├── common_types.go │ │ ├── doc.go │ │ ├── mysqlbackup_defaults.go │ │ ├── mysqlbackup_types.go │ │ ├── mysqlbackup_types_test.go │ │ ├── mysqlcluster_defaults.go │ │ ├── mysqlcluster_types.go │ │ ├── mysqlcluster_types_test.go │ │ ├── mysqldatabase_types.go │ │ ├── mysqldatabase_types_test.go │ │ ├── mysqluser_types.go │ │ ├── mysqluser_types_test.go │ │ ├── register.go │ │ ├── v1alpha1_suite_test.go │ │ ├── zz_generated.deepcopy.go │ │ └── zz_generated.defaults.go ├── controller │ ├── add_mysqlbackup.go │ ├── add_mysqlbackupcron.go │ ├── add_mysqlcluster.go │ ├── add_mysqldatabase.go │ ├── add_mysqluser.go │ ├── add_nodecontroller.go │ ├── add_orchestrator.go │ ├── controller.go │ ├── internal │ │ └── testutil │ │ │ ├── backup.go │ │ │ ├── cluster.go │ │ │ └── testutil.go │ ├── mysqlbackup │ │ ├── internal │ │ │ └── syncer │ │ │ │ ├── deletionjob.go │ │ │ │ ├── deletionjob_test.go │ │ │ │ ├── job.go │ │ │ │ ├── job_test.go │ │ │ │ └── syncer_suite_test.go │ │ ├── mysqlbackup_controller.go │ │ ├── mysqlbackup_controller_suite_test.go │ │ └── mysqlbackup_controller_test.go │ ├── mysqlbackupcron │ │ ├── job_backup.go │ │ ├── job_backup_test.go │ │ ├── mysqlbackupcron_controller.go │ │ ├── mysqlbackupcron_controller_suite_test.go │ │ └── mysqlbackupcron_controller_test.go │ ├── mysqlcluster │ │ ├── internal │ │ │ ├── cleaner │ │ │ │ ├── pvc.go │ │ │ │ ├── pvc_suite_test.go │ │ │ │ └── pvc_test.go │ │ │ ├── syncer │ │ │ │ ├── config_map.go │ │ │ │ ├── errors.go │ │ │ │ ├── headless_service.go │ │ │ │ ├── healthy_replicas_service.go │ │ │ │ ├── healthy_service.go │ │ │ │ ├── master_service.go │ │ │ │ ├── mysqlcluster_suite_test.go │ │ │ │ ├── pdb.go │ │ │ │ ├── pod.go │ │ │ │ ├── pod_test.go │ │ │ │ ├── secret.go │ │ │ │ ├── secret_operated.go │ │ │ │ ├── settings.go │ │ │ │ └── statefullset.go │ │ │ └── upgrades │ │ │ │ └── upgrades.go │ │ ├── mysqlcluster_controller.go │ │ ├── mysqlcluster_controller_suite_test.go │ │ └── mysqlcluster_controller_test.go │ ├── mysqldatabase │ │ ├── db_controller.go │ │ ├── db_controller_suite_test.go │ │ └── db_controller_test.go │ ├── mysqluser │ │ ├── mysqluser_controller.go │ │ ├── mysqluser_controller_suite_test.go │ │ └── mysqluser_controller_test.go │ ├── node │ │ ├── node_controller.go │ │ ├── node_ctrl_suite_test.go │ │ ├── node_ctrl_test.go │ │ ├── sql.go │ │ └── sql_fake_test.go │ └── orchestrator │ │ ├── orchestrator_controller.go │ │ ├── orchestrator_controller_suite_test.go │ │ ├── orchestrator_controller_test.go │ │ ├── orchestrator_reconcile.go │ │ ├── orchestrator_reconcile_test.go │ │ └── orchestrator_syncers.go ├── internal │ ├── mysql │ │ ├── database.go │ │ ├── escape.go │ │ ├── fake │ │ │ └── mysql.go │ │ ├── mysql.go │ │ ├── query.go │ │ ├── user.go │ │ └── user_test.go │ ├── mysqlbackup │ │ ├── defaults.go │ │ ├── mysqlbackup.go │ │ ├── mysqlbackup_test.go │ │ └── status.go │ ├── mysqlcluster │ │ ├── defaults.go │ │ ├── defaults_test.go │ │ ├── mysqlcluster.go │ │ ├── mysqlcluster_test.go │ │ ├── status.go │ │ └── validation.go │ ├── mysqldatabase │ │ └── mysqldatabase.go │ └── mysqluser │ │ └── mysqluser.go ├── options │ └── options.go ├── orc-helper │ └── helper.go ├── orchestrator │ ├── errors.go │ ├── fake │ │ └── client.go │ ├── instance.go │ ├── orchestrator.go │ └── util.go ├── sidecar │ ├── appclone.go │ ├── appclone_fakeserver_test.go │ ├── appclone_test.go │ ├── appconf.go │ ├── appconf_test.go │ ├── apphelper.go │ ├── apphelper_test.go │ ├── apptakebackup.go │ ├── configs.go │ ├── configs_test.go │ ├── constants.go │ ├── server.go │ ├── sidecar_suite_test.go │ └── util.go ├── testutil │ ├── factories │ │ ├── mysql_cluster.go │ │ ├── mysql_database.go │ │ └── mysql_user.go │ └── gomegamatcher │ │ └── gomegamatcher.go ├── util │ ├── constants │ │ └── constants.go │ └── util.go └── version │ └── version.go ├── skaffold.yaml └── test ├── e2e-values.yaml └── e2e ├── backups └── backups.go ├── cluster └── cluster.go ├── e2e.go ├── e2e_test.go ├── framework ├── backup_util.go ├── cleanup.go ├── cluster_util.go ├── framework.go ├── ginkgowrapper │ ├── BUILD │ └── wrapper.go ├── helm.go ├── portforward │ └── portforward.go ├── test_context.go └── util.go └── reporter.go /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | bin/* 3 | !bin/mysql-operator_linux_amd64 4 | !bin/mysql-operator-sidecar_linux_amd64 5 | !bin/orc-helper_linux_amd64 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | version.go export-subst 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: bitpoke 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C extensions 2 | *.so 3 | 4 | # Distribution / packaging 5 | .Python 6 | env/ 7 | develop-eggs/ 8 | dist/ 9 | downloads/ 10 | lib/ 11 | lib64/ 12 | parts/ 13 | sdist/ 14 | var/ 15 | 16 | # Unit test / coverage reports 17 | htmlcov/ 18 | .tox/ 19 | .coverage 20 | .coverage.* 21 | .cache 22 | nosetests.xml 23 | coverage.xml 24 | *.cover 25 | .hypothesis/ 26 | cover.out 27 | e2e-reports/ 28 | 29 | # Translations 30 | *.mo 31 | *.pot 32 | 33 | # Sphinx documentation 34 | docs/_build/ 35 | 36 | # emacs 37 | .\#* 38 | 39 | # vim 40 | *.swp 41 | 42 | # ignore bin 43 | /bin/ 44 | **/charts/*.tgz 45 | *.bak 46 | 47 | # ignore vscode 48 | .vscode 49 | 50 | # goland 51 | .idea 52 | 53 | # Test binary, build with `go test -c` 54 | *.test 55 | 56 | # Output of the go coverage tool, specifically when used with LiteIDE 57 | *.out 58 | 59 | # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 60 | .glide/ 61 | 62 | .DS_Store 63 | webroot/* 64 | 65 | .work 66 | .cache 67 | _output 68 | 69 | build 70 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | linters-settings: 2 | dupl: 3 | threshold: 400 4 | gocyclo: 5 | min-complexity: 10 6 | govet: 7 | check-shadowing: true 8 | lll: 9 | line-length: 170 10 | tab-width: 4 11 | gofmt: 12 | simplify: false 13 | 14 | run: 15 | tests: false 16 | skip-dirs: 17 | - vendor 18 | - test/e2e 19 | skip-files: 20 | - zz_generated.*.go 21 | 22 | 23 | 24 | linters: 25 | presets: 26 | - bugs 27 | - unused 28 | - format 29 | - style 30 | - complexity 31 | - performance 32 | 33 | # we should re-enable them and make lint pass 34 | disable: 35 | - goimports 36 | - maligned 37 | - gochecknoglobals 38 | # TODO: fix those linters (they were added for 1.42.1 upgrade) 39 | - sqlclosecheck 40 | - gosec 41 | - cyclop 42 | - forcetypeassert 43 | - revive 44 | - gofumpt 45 | - errorlint 46 | - exhaustivestruct 47 | - goerr113 48 | - gosimple 49 | - ifshort 50 | - noctx 51 | - predeclared 52 | - nlreturn 53 | - tagliatelle 54 | - wrapcheck 55 | - gci 56 | - nolintlint 57 | # TODO: fix those linters 58 | - whitespace 59 | - scopelint 60 | - wsl 61 | - gochecknoinits 62 | - godox 63 | - funlen 64 | - stylecheck 65 | - gocritic 66 | - gomnd 67 | - gocognit 68 | - godot 69 | - nestif 70 | 71 | issues: 72 | max-same-issues: 0 73 | exclude-use-default: false 74 | exclude: 75 | # gosec G104, about unhandled errors. We do that with errcheck already 76 | - "G104: Errors unhandled" 77 | exclude-rules: 78 | - linters: 79 | # Ignore package comments (ST1000) since most of the time are irrelevant 80 | - stylecheck 81 | text: "ST1000" 82 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | version: "2" 2 | domain: presslabs.org 3 | repo: github.com/bitpoke/mysql-operator 4 | resources: 5 | - group: mysql 6 | kind: MysqlCluster 7 | version: v1alpha1 8 | - group: mysql 9 | kind: MysqlBackup 10 | version: v1alpha1 11 | - group: mysql 12 | kind: MysqlDatabase 13 | version: v1alpha1 14 | - group: mysql 15 | kind: MysqlUser 16 | version: v1alpha1 17 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | closes/fixes #xyz 2 | 3 | --- 4 | - [ ] I've made sure the [CHANGELOG.md](https://github.com/presslabs/mysql-operator/blob/master/CHANGELOG.md) will remain up-to-date after this PR is merged. 5 | -------------------------------------------------------------------------------- /build/.drone.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: default 3 | 4 | clone: 5 | disable: true 6 | 7 | workspace: 8 | base: /workspace 9 | path: src/github.com/bitpoke/build 10 | 11 | steps: 12 | - name: clone 13 | image: plugins/git 14 | settings: 15 | depth: 0 16 | tags: true 17 | 18 | - name: install dependencies 19 | image: docker.io/bitpoke/build:v0.8.0 20 | commands: 21 | - make -j4 build.tools 22 | 23 | - name: build 24 | pull: true 25 | image: docker.io/bitpoke/build:v0.8.0 26 | commands: 27 | - dockerize -wait http://docker:2375/_ping -timeout 30s 28 | - make V=1 build 29 | 30 | - name: publish 31 | image: docker.io/bitpoke/build:v0.8.0 32 | commands: 33 | - /usr/local/bin/setup-credentials-helper.sh 34 | - make publish 35 | environment: 36 | DOCKER_USERNAME: bitpokebot 37 | DOCKER_PASSWORD: 38 | from_secret: DOCKER_PASSWORD 39 | when: 40 | ref: 41 | - refs/heads/master-* 42 | - refs/heads/release-* 43 | 44 | services: 45 | - name: docker 46 | image: docker:20.10.8-dind-rootless 47 | environment: 48 | DOCKER_TLS_CERTDIR: "" 49 | 50 | trigger: 51 | ref: 52 | - refs/pull/** 53 | - refs/heads/** 54 | -------------------------------------------------------------------------------- /build/.gitignore: -------------------------------------------------------------------------------- 1 | _output 2 | .cache 3 | .work 4 | /bin/ 5 | -------------------------------------------------------------------------------- /build/Makefile: -------------------------------------------------------------------------------- 1 | # Project Setup 2 | PROJECT_NAME := presslabs-build 3 | PROJECT_REPO := github.com/presslabs/build 4 | 5 | PLATFORMS = linux_amd64 6 | 7 | # this is required, since by default, the makelib files are under a ./build path prefix, but here, 8 | # they are under root 9 | ROOT_DIR := $(abspath $(shell cd ./ && pwd -P)) 10 | 11 | include makelib/common.mk 12 | 13 | IMAGES ?= build 14 | DOCKER_REGISTRY ?= docker.io/bitpoke 15 | 16 | include makelib/image.mk 17 | -------------------------------------------------------------------------------- /build/README.md: -------------------------------------------------------------------------------- 1 | # build 2 | bitpoke GNU make based build system 3 | 4 | ## Goals 5 | 6 | 1. Allow building locally the same way the project is build on CI 7 | 2. Provide a sane test, build, publish flow 8 | 3. Provide stable toolchain for building (eg. pinned tool versions) 9 | 4. Enables caching for speeding up builds. 10 | 11 | ## Quickstart 12 | 13 | ```sh 14 | git subtree add -P build https://github.com/bitpoke/build.git 15 | 16 | cat < Makefile 17 | # Project Setup 18 | PROJECT_NAME := mysql-operator 19 | PROJECT_REPO := github.com/bitpoke/mysql-operator 20 | 21 | include build/makelib/common.mk 22 | ``` 23 | 24 | ## Usage 25 | 26 | ``` 27 | Usage: make [make-options] [options] 28 | 29 | Common Targets: 30 | build Build source code and other artifacts for host platform. 31 | build.all Build source code and other artifacts for all platforms. 32 | build.tools Install the required build tools. 33 | clean Remove all files created during the build. 34 | distclean Remove all files created during the build including cached tools. 35 | generate Run code generation tools. 36 | fmt Run code auto-formatting tools. 37 | lint Run lint and code analysis tools. 38 | test Runs unit tests. 39 | e2e Runs end-to-end integration tests. 40 | translate Collect translation strings and post-process the .pot/.po files. 41 | help Show this help info. 42 | ``` 43 | 44 | ## Acknowledgement 45 | 46 | This work is based on https://github.com/upbound/build. 47 | -------------------------------------------------------------------------------- /build/bin/po-diff.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 Pressinfra SRL 4 | # 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 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | export ROOT_DIR=$(dirname "${BASH_SOURCE}")/../.. 22 | 23 | # Install tools we need, but only from vendor/... 24 | cd "${ROOT_DIR}" 25 | 26 | diff -u \ 27 | <(grep -E '^msgid' "${1}" | sort | sed 's/msgid[[:space:]]*//g') \ 28 | <(grep -E '^msgid' "${2}" | sort | sed 's/msgid[[:space:]]*//g') 29 | 30 | exit 0 31 | -------------------------------------------------------------------------------- /build/images/build/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORMS := linux_amd64 2 | include ../../makelib/common.mk 3 | 4 | # this is required, since by default, the makelib files are under a ./build path prefix, but here, 5 | # they are under root 6 | ROOT_DIR := $(abspath $(shell cd ./../.. && pwd -P)) 7 | 8 | IMAGE = $(BUILD_REGISTRY)/build-$(ARCH) 9 | CACHE_IMAGES = $(IMAGE) 10 | include ../../makelib/image.mk 11 | 12 | img.build: 13 | @$(INFO) docker build $(IMAGE) $(IMAGE_PLATFORM) 14 | @cp -La . $(IMAGE_TEMP_DIR) 15 | @mkdir -p $(IMAGE_TEMP_DIR)/rootfs 16 | @docker buildx build $(BUILD_ARGS) \ 17 | --platform $(IMAGE_PLATFORM) \ 18 | -t $(IMAGE) \ 19 | --build-arg ARCH=$(ARCH) \ 20 | $(IMAGE_TEMP_DIR) 21 | @$(OK) docker build $(IMAGE) 22 | -------------------------------------------------------------------------------- /build/images/build/build-scripts/install-packages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 Pressinfra 4 | # 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 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -o errexit 18 | set -o nounset 19 | set -o pipefail 20 | 21 | apt-get update 22 | apt-get install -yy -q --no-install-recommends "${@}" 23 | apt-get clean 24 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 25 | -------------------------------------------------------------------------------- /build/images/build/root/build/rsyncd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Copyright 2016 The Upbound Authors. All rights reserved. 4 | # 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 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | VOLUME=${VOLUME:-/volume} 18 | ALLOW=${ALLOW:-192.168.0.0/16 172.16.0.0/12 10.0.0.0/8} 19 | OWNER=${OWNER:-nobody} 20 | GROUP=${GROUP:-nogroup} 21 | 22 | if [[ "${GROUP}" != "nogroup" && "${GROUP}" != "root" ]]; then 23 | groupadd -g ${GROUP} rsync 24 | fi 25 | 26 | if [[ "${OWNER}" != "nobody" && "${OWNER}" != "root" ]]; then 27 | groupadd -u ${OWNER} -G rsync rsync 28 | fi 29 | 30 | chown "${OWNER}:${GROUP}" "${VOLUME}" 31 | 32 | [ -f /etc/rsyncd.conf ] || cat < /etc/rsyncd.conf 33 | uid = ${OWNER} 34 | gid = ${GROUP} 35 | use chroot = yes 36 | log file = /dev/stdout 37 | reverse lookup = no 38 | [volume] 39 | hosts deny = * 40 | hosts allow = ${ALLOW} 41 | read only = false 42 | path = ${VOLUME} 43 | comment = volume 44 | EOF 45 | 46 | for dir in ${MKDIRS}; do 47 | mkdir -p ${dir} 48 | chown "${OWNER}:${GROUP}" ${dir} 49 | done 50 | 51 | exec /usr/bin/rsync --no-detach --daemon --config /etc/rsyncd.conf "$@" 52 | -------------------------------------------------------------------------------- /build/images/build/root/build/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # Copyright 2016 The Upbound Authors. All rights reserved. 4 | # 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 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | ARGS="$@" 18 | if [ $# -eq 0 ]; then 19 | ARGS=/bin/bash 20 | fi 21 | 22 | BUILDER_USER=${BUILDER_USER:-upbound} 23 | BUILDER_GROUP=${BUILDER_GROUP:-upbound} 24 | BUILDER_UID=${BUILDER_UID:-1000} 25 | BUILDER_GID=${BUILDER_GID:-1000} 26 | 27 | groupadd -o -g $BUILDER_GID $BUILDER_GROUP 2> /dev/null 28 | useradd -o -m -g $BUILDER_GID -u $BUILDER_UID $BUILDER_USER 2> /dev/null 29 | echo "$BUILDER_USER ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 30 | export HOME=/home/${BUILDER_USER} 31 | echo "127.0.0.1 $(cat /etc/hostname)" >> /etc/hosts 32 | [[ -S /var/run/docker.sock ]] && chmod 666 /var/run/docker.sock 33 | chown -R $BUILDER_UID:$BUILDER_GID $HOME 34 | exec chpst -u :$BUILDER_UID:$BUILDER_GID ${ARGS} 35 | -------------------------------------------------------------------------------- /build/images/build/root/usr/local/bin/xvfb-chrome: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2016 The Upbound Authors. All rights reserved. 4 | # 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 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | _kill_procs() { 18 | kill -TERM $chrome 19 | wait $chrome 20 | } 21 | 22 | # Setup a trap to catch SIGTERM and relay it to child processes 23 | trap _kill_procs SIGTERM 24 | 25 | # Start Chrome inside xvfb 26 | xvfb-run -a -s "-screen 0 1920x1080x24 -nolisten tcp" /opt/google/chrome/chrome --no-sandbox $@ & 27 | chrome=$! 28 | 29 | wait $chrome 30 | -------------------------------------------------------------------------------- /build/makelib/cache.mk: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Pressinfra SRL. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 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 | ifndef __CACHE_MAKEFILE__ 16 | __CACHE_MAKEFILE__ := included 17 | 18 | RCLONE_BIN ?= RCLONE_VERSION=true rclone 19 | RCLONE_ARGS ?= -q --config /dev/null 20 | 21 | ifeq ($(CACHE_BACKEND),) 22 | $(error You must define CACHE_BACKEND before adding cache support. See format at https://rclone.org/docs/#backend-path-to-dir) 23 | endif 24 | 25 | CACHE_COMPRESSION ?= gzip 26 | 27 | ifneq ($(DRONE_PULL_REQUEST),) 28 | CACHE_NAME ?= $(PROJECT_NAME)-pr$(DRONE_PULL_REQUEST)-cache 29 | else ifneq ($(DRONE_TAG),) 30 | CACHE_NAME ?= $(PROJECT_NAME)-$(DRONE_TAG)-cache 31 | else 32 | CACHE_NAME ?= $(PROJECT_NAME)-$(BRANCH_NAME)-cache 33 | endif 34 | 35 | 36 | RCLONE := $(RCLONE_BIN) $(RCLONE_ARGS) 37 | 38 | ifeq ($(CACHE_COMPRESSION),gzip) 39 | TAR_COMPRESS_ARGS += -z 40 | CACHE_EXTENSION_SUFFIX := .gz 41 | endif 42 | 43 | CACHE_FILE := $(CACHE_NAME).tar$(CACHE_EXTENSION_SUFFIX) 44 | 45 | .PHONY: cache.store cache.restore 46 | 47 | cache.store: 48 | @$(INFO) storing cache $(CACHE_FILE) into $(CACHE_BACKEND) 49 | @$(RCLONE) mkdir $(CACHE_BACKEND) || $(FAIL) 50 | @tar -C $(CACHE_DIR) $(TAR_COMPRESS_ARGS) -cf - ./ | $(RCLONE) rcat $(CACHE_BACKEND)/$(CACHE_FILE) || $(FAIL) 51 | @$(OK) cache store 52 | 53 | cache.restore: |$(CACHE_DIR) 54 | @$(INFO) restoring cache from $(CACHE_BACKEND)/$(CACHE_FILE) 55 | @$(RCLONE) cat $(CACHE_BACKEND)/$(CACHE_FILE) | tar -C $(CACHE_DIR) $(TAR_COMPRESS_ARGS) -x \ 56 | && $(OK) cache restore \ 57 | || $(WARN) cache restore failed 58 | 59 | endif # __CACHE_MAKEFILE__ 60 | -------------------------------------------------------------------------------- /build/makelib/gcp.mk: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Pressinfra SRL. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 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 | ifndef __GOOGLE_CLOUD_MAKEFILE__ 16 | __GOOGLE_CLOUD_MAKEFILE__ := included 17 | 18 | ifeq ($(origin GOOGLE_CLOUD_PROJECT),undefined) 19 | ifneq ($(GCLOUD_PROJECT),) 20 | GOOGLE_CLOUD_PROJECT := $(GCLOUD_PROJECT) 21 | else 22 | GOOGLE_CLOUD_PROJECT := $(shell gcloud config get-value project) 23 | endif 24 | endif 25 | 26 | ifeq ($(GOOGLE_CLOUD_PROJECT),) 27 | $(error Could not determine current Google Cloud Project. Set the GOOGLE_CLOUD_PROJECT environment variable or set with `gcloud config`) 28 | else 29 | export GOOGLE_CLOUD_PROJECT 30 | endif 31 | 32 | endif # __GOOGLE_CLOUD_MAKEFILE__ 33 | -------------------------------------------------------------------------------- /build/makelib/gettext.mk: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Upbound Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 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 | ifndef __GETTEXT_MAKEFILE__ 16 | __GETTEXT_MAKEFILE__ := included 17 | 18 | # ==================================================================================== 19 | # Options 20 | 21 | # ==================================================================================== 22 | # Translations 23 | 24 | # The list of languages to generate translations for 25 | LANGUAGES ?= 26 | 27 | LOCALES_DIR ?= $(ROOT_DIR)/locales 28 | $(LOCALES_DIR): 29 | @mkdir -p $(LOCALES_DIR) 30 | 31 | ifeq ($(LANGUAGES),) 32 | $(error You must specify the LANGUAGES variable in order to handle translations) 33 | endif 34 | 35 | ifeq ($(HOSTOS),darwin) 36 | MSGFMT = /usr/local/opt/gettext/bin/msgfmt 37 | MSGMERGE = /usr/local/opt/gettext/bin/msgmerge 38 | else 39 | MSGFMT = msgfmt 40 | MSGMERGE = msgmerge 41 | endif 42 | 43 | PO_FILES := $(shell find $(LOCALES_DIR) -name '*.po') 44 | POT_FILES := $(shell find $(LOCALES_DIR) -mindepth 1 -maxdepth 1 -name '*.pot') 45 | 46 | # lint the code 47 | $(eval $(call common.target,translations)) 48 | 49 | gettext.lint: 50 | @$(INFO) msgfmt check 51 | $(foreach p,$(PO_FILES),@$(MSGFMT) -c $(p) || $(FAIL) ${\n}) 52 | @$(OK) msgfmt check 53 | 54 | .gettext.merge: 55 | @$(INFO) msgmerge 56 | $(foreach l,$(LANGUAGES),@mkdir -p $(LOCALES_DIR)/$(l) || $(FAIL) ${\n}) 57 | $(foreach pot,$(POT_FILES),$(foreach l,$(LANGUAGES), \ 58 | @touch $(LOCALES_DIR)/$(l)/$(basename $(notdir $(pot))).po || $(FAIL) ${\n} \ 59 | @$(MSGMERGE) -q --no-wrap --sort-output --no-fuzzy-matching --lang=$(l) -U "$(LOCALES_DIR)/$(l)/$(basename $(notdir $(pot))).po" "$(pot)" || $(FAIL) ${\n} \ 60 | )) 61 | @find $(LOCALES_DIR) -name '*.po~' -delete 62 | @find $(LOCALES_DIR) -name '*.pot~' -delete 63 | @$(OK) msgmerge 64 | 65 | .gettext.build: 66 | @$(INFO) copying translations 67 | @rm -rf $(OUTPUT_DIR)/locales 68 | @cp -a $(LOCALES_DIR) $(OUTPUT_DIR)/locales 69 | @$(OK) copying translations 70 | 71 | .PHONY: gettext.lint .gettext.build .gettext.merge 72 | 73 | # ==================================================================================== 74 | # Common Targets 75 | .lint.run: gettext.lint 76 | 77 | .translations.run: .gettext.merge 78 | 79 | .build.code: .gettext.build 80 | 81 | endif # __GETTEXT_MAKEFILE__ 82 | -------------------------------------------------------------------------------- /build/makelib/k8s-tools.mk: -------------------------------------------------------------------------------- 1 | # Copyright 2016 The Upbound Authors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 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 | ifndef __K8S_TOOLS_MAKEFILE__ 16 | __K8S_TOOLS_MAKEFILE__ := included 17 | 18 | # ==================================================================================== 19 | # tools 20 | 21 | # kubectl download and install 22 | KUBECTL_VERSION ?= 1.19.13 23 | KUBECTL_DOWNLOAD_URL ?= https://storage.googleapis.com/kubernetes-release/release/v$(KUBECTL_VERSION)/bin/$(HOSTOS)/$(HOSTARCH)/kubectl 24 | $(eval $(call tool.download,kubectl,$(KUBECTL_VERSION),$(KUBECTL_DOWNLOAD_URL))) 25 | 26 | # kind download and install 27 | KIND_VERSION ?= 0.11.1 28 | KIND_DOWNLOAD_URL ?= https://github.com/kubernetes-sigs/kind/releases/download/v$(KIND_VERSION)/kind-$(HOSTOS)-$(HOSTARCH) 29 | $(eval $(call tool.download,kind,$(KIND_VERSION),$(KIND_DOWNLOAD_URL))) 30 | 31 | # kind download and install 32 | KUSTOMIZE_VERSION ?= 4.2.0 33 | KUSTOMIZE_DOWNLOAD_URL ?=https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v$(KUSTOMIZE_VERSION)/kustomize_v$(KUSTOMIZE_VERSION)_$(HOST_PLATFORM).tar.gz 34 | $(eval $(call tool.download.tar.gz,kustomize,$(KUSTOMIZE_VERSION),$(KUSTOMIZE_DOWNLOAD_URL),kustomize,0)) 35 | 36 | endif # __K8S_TOOLS_MAKEFILE__ 37 | 38 | -------------------------------------------------------------------------------- /config/certmanager/certificate.yaml: -------------------------------------------------------------------------------- 1 | # The following manifests contain a self-signed issuer CR and a certificate CR. 2 | # More document can be found at https://docs.cert-manager.io 3 | # WARNING: Targets CertManager 0.11 check https://docs.cert-manager.io/en/latest/tasks/upgrading/index.html for 4 | # breaking changes 5 | apiVersion: cert-manager.io/v1alpha2 6 | kind: Issuer 7 | metadata: 8 | name: selfsigned-issuer 9 | namespace: system 10 | spec: 11 | selfSigned: {} 12 | --- 13 | apiVersion: cert-manager.io/v1alpha2 14 | kind: Certificate 15 | metadata: 16 | name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml 17 | namespace: system 18 | spec: 19 | # $(SERVICE_NAME) and $(SERVICE_NAMESPACE) will be substituted by kustomize 20 | dnsNames: 21 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc 22 | - $(SERVICE_NAME).$(SERVICE_NAMESPACE).svc.cluster.local 23 | issuerRef: 24 | kind: Issuer 25 | name: selfsigned-issuer 26 | secretName: webhook-server-cert # this secret will not be prefixed, since it's not managed by kustomize 27 | -------------------------------------------------------------------------------- /config/certmanager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - certificate.yaml 3 | 4 | configurations: 5 | - kustomizeconfig.yaml 6 | -------------------------------------------------------------------------------- /config/certmanager/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This configuration is for teaching kustomize how to update name ref and var substitution 2 | nameReference: 3 | - kind: Issuer 4 | group: cert-manager.io 5 | fieldSpecs: 6 | - kind: Certificate 7 | group: cert-manager.io 8 | path: spec/issuerRef/name 9 | 10 | varReference: 11 | - kind: Certificate 12 | group: cert-manager.io 13 | path: spec/commonName 14 | - kind: Certificate 15 | group: cert-manager.io 16 | path: spec/dnsNames 17 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/mysql.presslabs.org_mysqlclusters.yaml 6 | - bases/mysql.presslabs.org_mysqlbackups.yaml 7 | - bases/mysql.presslabs.org_mysqlusers.yaml 8 | - bases/mysql.presslabs.org_mysqldatabases.yaml 9 | # +kubebuilder:scaffold:crdkustomizeresource 10 | 11 | patchesStrategicMerge: 12 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 13 | # patches here are for enabling the conversion webhook for each CRD 14 | #- patches/webhook_in_mysqlclusters.yaml 15 | #- patches/webhook_in_mysqlbackups.yaml 16 | #- patches/webhook_in_mysqlusers.yaml 17 | #- patches/webhook_in_mysqldatabases.yaml 18 | # +kubebuilder:scaffold:crdkustomizewebhookpatch 19 | 20 | # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. 21 | # patches here are for enabling the CA injection for each CRD 22 | #- patches/cainjection_in_mysqlclusters.yaml 23 | #- patches/cainjection_in_mysqlbackups.yaml 24 | #- patches/cainjection_in_mysqlusers.yaml 25 | #- patches/cainjection_in_mysqldatabases.yaml 26 | # +kubebuilder:scaffold:crdkustomizecainjectionpatch 27 | 28 | # the following config is for teaching kustomize how to do kustomization for CRDs. 29 | configurations: 30 | - kustomizeconfig.yaml 31 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | group: apiextensions.k8s.io 8 | path: spec/conversion/webhookClientConfig/service/name 9 | 10 | namespace: 11 | - kind: CustomResourceDefinition 12 | group: apiextensions.k8s.io 13 | path: spec/conversion/webhookClientConfig/service/namespace 14 | create: false 15 | 16 | varReference: 17 | - path: metadata/annotations 18 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_mysqlbackups.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: mysqlbackups.mysql.presslabs.org 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_mysqlclusters.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: mysqlclusters.mysql.presslabs.org 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_mysqldatabases.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: mysqldatabases.mysql.presslabs.org 9 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_mysqlusers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | annotations: 7 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 8 | name: mysqlusers.mysql.presslabs.org 9 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_mysqlbackups.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: mysqlbackups.mysql.presslabs.org 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhookClientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_mysqlclusters.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: mysqlclusters.mysql.presslabs.org 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhookClientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_mysqldatabases.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: mysqldatabases.mysql.presslabs.org 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhookClientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_mysqlusers.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables conversion webhook for CRD 2 | # CRD conversion requires k8s 1.13 or later. 3 | apiVersion: apiextensions.k8s.io/v1beta1 4 | kind: CustomResourceDefinition 5 | metadata: 6 | name: mysqlusers.mysql.presslabs.org 7 | spec: 8 | conversion: 9 | strategy: Webhook 10 | webhookClientConfig: 11 | # this is "\n" used as a placeholder, otherwise it will be rejected by the apiserver for being blank, 12 | # but we're going to set it later using the cert-manager (or potentially a patch if not using cert-manager) 13 | caBundle: Cg== 14 | service: 15 | namespace: system 16 | name: webhook-service 17 | path: /convert 18 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: mysql-operator-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: mysql-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 20 | # crd/kustomization.yaml 21 | #- ../webhook 22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 23 | #- ../certmanager 24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 25 | #- ../prometheus 26 | 27 | patchesStrategicMerge: 28 | # Protect the /metrics endpoint by putting it behind auth. 29 | # If you want your controller-manager to expose the /metrics 30 | # endpoint w/o any authn/z, please comment the following line. 31 | - manager_auth_proxy_patch.yaml 32 | 33 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 34 | # crd/kustomization.yaml 35 | #- manager_webhook_patch.yaml 36 | 37 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 38 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 39 | # 'CERTMANAGER' needs to be enabled to use ca injection 40 | #- webhookcainjection_patch.yaml 41 | 42 | # the following config is for teaching kustomize how to do var substitution 43 | vars: 44 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 45 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 46 | # objref: 47 | # kind: Certificate 48 | # group: cert-manager.io 49 | # version: v1alpha2 50 | # name: serving-cert # this name should match the one in certificate.yaml 51 | # fieldref: 52 | # fieldpath: metadata.namespace 53 | #- name: CERTIFICATE_NAME 54 | # objref: 55 | # kind: Certificate 56 | # group: cert-manager.io 57 | # version: v1alpha2 58 | # name: serving-cert # this name should match the one in certificate.yaml 59 | #- name: SERVICE_NAMESPACE # namespace of the service 60 | # objref: 61 | # kind: Service 62 | # version: v1 63 | # name: webhook-service 64 | # fieldref: 65 | # fieldpath: metadata.namespace 66 | #- name: SERVICE_NAME 67 | # objref: 68 | # kind: Service 69 | # version: v1 70 | # name: webhook-service 71 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | name: https 22 | - name: manager 23 | args: 24 | - "--metrics-addr=127.0.0.1:8080" 25 | - "--enable-leader-election" 26 | -------------------------------------------------------------------------------- /config/default/manager_webhook_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | ports: 12 | - containerPort: 9443 13 | name: webhook-server 14 | protocol: TCP 15 | volumeMounts: 16 | - mountPath: /tmp/k8s-webhook-server/serving-certs 17 | name: cert 18 | readOnly: true 19 | volumes: 20 | - name: cert 21 | secret: 22 | defaultMode: 420 23 | secretName: webhook-server-cert 24 | -------------------------------------------------------------------------------- /config/default/webhookcainjection_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch add annotation to admission webhook config and 2 | # the variables $(CERTIFICATE_NAMESPACE) and $(CERTIFICATE_NAME) will be substituted by kustomize. 3 | apiVersion: admissionregistration.k8s.io/v1beta1 4 | kind: MutatingWebhookConfiguration 5 | metadata: 6 | name: mutating-webhook-configuration 7 | annotations: 8 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 9 | --- 10 | apiVersion: admissionregistration.k8s.io/v1beta1 11 | kind: ValidatingWebhookConfiguration 12 | metadata: 13 | name: validating-webhook-configuration 14 | annotations: 15 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 16 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | labels: 23 | control-plane: controller-manager 24 | spec: 25 | containers: 26 | - command: 27 | - /manager 28 | args: 29 | - --enable-leader-election 30 | image: controller:latest 31 | name: manager 32 | resources: 33 | limits: 34 | cpu: 100m 35 | memory: 30Mi 36 | requests: 37 | cpu: 100m 38 | memory: 20Mi 39 | terminationGracePeriodSeconds: 10 40 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | selector: 15 | matchLabels: 16 | control-plane: controller-manager 17 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1beta1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: ["/metrics"] 7 | verbs: ["get"] 8 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: ["authentication.k8s.io"] 7 | resources: 8 | - tokenreviews 9 | verbs: ["create"] 10 | - apiGroups: ["authorization.k8s.io"] 11 | resources: 12 | - subjectaccessreviews 13 | verbs: ["create"] 14 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | targetPort: https 13 | selector: 14 | control-plane: controller-manager 15 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - role.yaml 3 | - role_binding.yaml 4 | - leader_election_role.yaml 5 | - leader_election_role_binding.yaml 6 | # Comment the following 4 lines if you want to disable 7 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 8 | # which protects your /metrics endpoint. 9 | - auth_proxy_service.yaml 10 | - auth_proxy_role.yaml 11 | - auth_proxy_role_binding.yaml 12 | - auth_proxy_client_clusterrole.yaml 13 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - configmaps/status 23 | verbs: 24 | - get 25 | - update 26 | - patch 27 | - apiGroups: 28 | - "" 29 | resources: 30 | - events 31 | verbs: 32 | - create 33 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/mysqlbackup_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit mysqlbackups. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqlbackup-editor-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqlbackups 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - mysql.presslabs.org 21 | resources: 22 | - mysqlbackups/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/mysqlbackup_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view mysqlbackups. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqlbackup-viewer-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqlbackups 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - mysql.presslabs.org 17 | resources: 18 | - mysqlbackups/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/mysqlcluster_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit mysqlclusters. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqlcluster-editor-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqlclusters 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - mysql.presslabs.org 21 | resources: 22 | - mysqlclusters/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/mysqlcluster_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view mysqlclusters. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqlcluster-viewer-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqlclusters 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - mysql.presslabs.org 17 | resources: 18 | - mysqlclusters/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/mysqldatabase_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit mysqldatabases. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqldatabase-editor-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqldatabases 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - mysql.presslabs.org 21 | resources: 22 | - mysqldatabases/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/mysqldatabase_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view mysqldatabases. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqldatabase-viewer-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqldatabases 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - mysql.presslabs.org 17 | resources: 18 | - mysqldatabases/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/mysqluser_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit mysqlusers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqluser-editor-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqlusers 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - mysql.presslabs.org 21 | resources: 22 | - mysqlusers/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/mysqluser_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view mysqlusers. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: mysqluser-viewer-role 6 | rules: 7 | - apiGroups: 8 | - mysql.presslabs.org 9 | resources: 10 | - mysqlusers 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - mysql.presslabs.org 17 | resources: 18 | - mysqlusers/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | 2 | --- 3 | apiVersion: rbac.authorization.k8s.io/v1 4 | kind: ClusterRole 5 | metadata: 6 | creationTimestamp: null 7 | name: manager-role 8 | rules: 9 | - apiGroups: 10 | - apps 11 | resources: 12 | - statefulsets 13 | verbs: 14 | - create 15 | - delete 16 | - get 17 | - list 18 | - patch 19 | - update 20 | - watch 21 | - apiGroups: 22 | - batch 23 | resources: 24 | - jobs 25 | verbs: 26 | - create 27 | - delete 28 | - get 29 | - list 30 | - patch 31 | - update 32 | - watch 33 | - apiGroups: 34 | - coordination.k8s.io 35 | resources: 36 | - leases 37 | verbs: 38 | - create 39 | - delete 40 | - get 41 | - list 42 | - patch 43 | - update 44 | - watch 45 | - apiGroups: 46 | - "" 47 | resources: 48 | - configmaps 49 | - events 50 | - jobs 51 | - persistentvolumeclaims 52 | - pods 53 | - secrets 54 | - services 55 | verbs: 56 | - create 57 | - delete 58 | - get 59 | - list 60 | - patch 61 | - update 62 | - watch 63 | - apiGroups: 64 | - "" 65 | resources: 66 | - pods/status 67 | verbs: 68 | - create 69 | - delete 70 | - get 71 | - list 72 | - patch 73 | - update 74 | - watch 75 | - apiGroups: 76 | - mysql.presslabs.org 77 | resources: 78 | - mysqlbackups 79 | - mysqlbackups/finalizers 80 | - mysqlbackups/status 81 | verbs: 82 | - create 83 | - delete 84 | - get 85 | - list 86 | - patch 87 | - update 88 | - watch 89 | - apiGroups: 90 | - mysql.presslabs.org 91 | resources: 92 | - mysqlclusters 93 | - mysqlclusters/finalizers 94 | - mysqlclusters/status 95 | verbs: 96 | - create 97 | - delete 98 | - get 99 | - list 100 | - patch 101 | - update 102 | - watch 103 | - apiGroups: 104 | - mysql.presslabs.org 105 | resources: 106 | - mysqldatabases 107 | - mysqldatabases/finalizers 108 | - mysqldatabases/status 109 | verbs: 110 | - create 111 | - delete 112 | - get 113 | - list 114 | - patch 115 | - update 116 | - watch 117 | - apiGroups: 118 | - mysql.presslabs.org 119 | resources: 120 | - mysqlusers 121 | - mysqlusers/status 122 | verbs: 123 | - create 124 | - delete 125 | - get 126 | - list 127 | - patch 128 | - update 129 | - watch 130 | - apiGroups: 131 | - policy 132 | resources: 133 | - poddisruptionbudgets 134 | verbs: 135 | - create 136 | - delete 137 | - get 138 | - list 139 | - patch 140 | - update 141 | - watch 142 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: default 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/samples/mysql_v1alpha1_mysqlbackup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlBackup 3 | metadata: 4 | name: mysqlbackup-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/samples/mysql_v1alpha1_mysqlcluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlCluster 3 | metadata: 4 | name: mysqlcluster-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/samples/mysql_v1alpha1_mysqldatabase.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlDatabase 3 | metadata: 4 | name: mysqldatabase-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/samples/mysql_v1alpha1_mysqluser.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlUser 3 | metadata: 4 | name: mysqluser-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/webhook/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manifests.yaml 3 | - service.yaml 4 | 5 | configurations: 6 | - kustomizeconfig.yaml 7 | -------------------------------------------------------------------------------- /config/webhook/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # the following config is for teaching kustomize where to look at when substituting vars. 2 | # It requires kustomize v2.1.0 or newer to work properly. 3 | nameReference: 4 | - kind: Service 5 | version: v1 6 | fieldSpecs: 7 | - kind: MutatingWebhookConfiguration 8 | group: admissionregistration.k8s.io 9 | path: webhooks/clientConfig/service/name 10 | - kind: ValidatingWebhookConfiguration 11 | group: admissionregistration.k8s.io 12 | path: webhooks/clientConfig/service/name 13 | 14 | namespace: 15 | - kind: MutatingWebhookConfiguration 16 | group: admissionregistration.k8s.io 17 | path: webhooks/clientConfig/service/namespace 18 | create: true 19 | - kind: ValidatingWebhookConfiguration 20 | group: admissionregistration.k8s.io 21 | path: webhooks/clientConfig/service/namespace 22 | create: true 23 | 24 | varReference: 25 | - path: metadata/annotations 26 | -------------------------------------------------------------------------------- /config/webhook/service.yaml: -------------------------------------------------------------------------------- 1 | 2 | apiVersion: v1 3 | kind: Service 4 | metadata: 5 | name: webhook-service 6 | namespace: system 7 | spec: 8 | ports: 9 | - port: 443 10 | targetPort: 9443 11 | selector: 12 | control-plane: controller-manager 13 | -------------------------------------------------------------------------------- /deploy/charts/mysql-cluster/.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 | -------------------------------------------------------------------------------- /deploy/charts/mysql-cluster/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | appVersion: "1.0" 3 | description: A Helm chart for easy deployment of a MySQL cluster with MySQL operator. 4 | name: mysql-cluster 5 | version: 0.3.1 6 | -------------------------------------------------------------------------------- /deploy/charts/mysql-cluster/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "mysql-cluster.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 "mysql-cluster.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 chart name and version as used by the chart label. 29 | */}} 30 | {{- define "mysql-cluster.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{- define "mysql-cluster.dbConnectURL" -}} 35 | mysql:// 36 | {{- if .Values.appUser -}} 37 | {{ urlquery .Values.appUser -}} 38 | {{- if .Values.appPassword -}} 39 | :{{ urlquery .Values.appPassword }} 40 | {{- end -}} 41 | @ 42 | {{- end -}} 43 | {{- include "mysql-cluster.clusterName" . -}}-mysql-master:3306 44 | {{- if .Values.appDatabase -}} 45 | /{{- .Values.appDatabase -}} 46 | {{- end -}} 47 | {{- end -}} 48 | 49 | {{- define "mysql-cluster.clusterName" -}} 50 | {{- printf "%s-db" (include "mysql-cluster.fullname" .) | trunc 63 | trimSuffix "-" -}} 51 | {{- end -}} 52 | 53 | {{- define "mysql-cluster.secretName" -}} 54 | {{- printf "%s-db" (include "mysql-cluster.fullname" .) | trunc 63 | trimSuffix "-" -}} 55 | {{- end -}} 56 | 57 | {{- define "mysql-cluster.backupSecretName" -}} 58 | {{- printf "%s-db-backup" (include "mysql-cluster.fullname" .) | trunc 63 | trimSuffix "-" -}} 59 | {{- end -}} 60 | -------------------------------------------------------------------------------- /deploy/charts/mysql-cluster/templates/backup-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.backupCredentials }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ template "mysql-cluster.backupSecretName" . }} 6 | labels: 7 | app: {{ template "mysql-cluster.name" . }} 8 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | {{- if .Values.backupSecretLabels }} 12 | {{- toYaml .Values.backupSecretLabels | nindent 4 }} 13 | {{- end }} 14 | {{- if .Values.backupSecretAnnotations }} 15 | annotations: 16 | {{ toYaml .Values.backupSecretAnnotations }} 17 | {{- end }} 18 | type: Opaque 19 | data: 20 | {{- range $key, $value := .Values.backupCredentials }} 21 | {{ $key | upper }}: {{ $value | b64enc | quote }} 22 | {{- end }} 23 | {{- end -}} 24 | -------------------------------------------------------------------------------- /deploy/charts/mysql-cluster/templates/cluster.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlCluster 3 | metadata: 4 | name: {{ include "mysql-cluster.clusterName" . }} 5 | labels: 6 | app: {{ template "mysql-cluster.name" . }} 7 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 8 | release: {{ .Release.Name }} 9 | heritage: {{ .Release.Service }} 10 | spec: 11 | replicas: {{ .Values.replicas }} 12 | 13 | {{- if .Values.customSecretName }} 14 | secretName: {{ .Values.customSecretName }} 15 | {{- else }} 16 | secretName: {{ include "mysql-cluster.secretName" . }} 17 | {{- end }} 18 | 19 | {{- if .Values.image }} 20 | image: {{ .Values.image }} 21 | {{- end }} 22 | 23 | {{- if .Values.mysqlVersion }} 24 | mysqlVersion: {{ .Values.mysqlVersion | quote }} 25 | {{- end }} 26 | 27 | {{- if .Values.initBucketURL }} 28 | initBucketURL: {{ .Values.initBucketURL }} 29 | {{- end }} 30 | {{- if .Values.initBucketSecretName }} 31 | initBucketSecretName: {{ .Values.initBucketSecretName }} 32 | {{- end }} 33 | 34 | {{- if .Values.backupSecretName }} 35 | backupSecretName: {{ .Values.backupSecretName }} 36 | {{- else if .Values.backupCredentials }} 37 | backupSecretName: {{ include "mysql-cluster.backupSecretName" . }} 38 | {{- else if .Values.backupSchedule }} 39 | {{ required "One of .mysql.backupSecretName and .mysql.backupCredentials should be specified" "" }} 40 | {{- end }} 41 | 42 | {{- if .Values.serverIDOffset }} 43 | serverIDOffset: {{ .Values.serverIDOffset }} 44 | {{- end }} 45 | 46 | {{- if .Values.backupSchedule }} 47 | backupSchedule: "{{ .Values.backupSchedule }}" 48 | backupRemoteDeletePolicy: {{ .Values.backupRemoteDeletePolicy }} 49 | backupURL: {{ required ".mysql.backupURL is missing" .Values.backupURL }} 50 | {{- end }} 51 | {{- if .Values.backupScheduleJobsHistoryLimit }} 52 | backupScheduleJobsHistoryLimit: {{ .Values.backupScheduleJobsHistoryLimit }} 53 | {{- end }} 54 | 55 | {{- if .Values.mysqlConf }} 56 | mysqlConf: 57 | {{- toYaml .Values.mysqlConf | nindent 4 }} 58 | {{- end }} 59 | 60 | {{- if .Values.podSpec }} 61 | podSpec: 62 | {{- toYaml .Values.podSpec | nindent 4 }} 63 | {{- end }} 64 | 65 | {{- if .Values.volumeSpec }} 66 | volumeSpec: 67 | {{- toYaml .Values.volumeSpec | nindent 4 }} 68 | {{- end }} 69 | 70 | {{- if .Values.initFileExtraSQL }} 71 | initFileExtraSQL: 72 | {{- toYaml .Values.initFileExtraSQL | nindent 6 }} 73 | {{- end }} 74 | 75 | {{- if .Values.queryLimits }} 76 | queryLimits: 77 | {{ toYaml .Values.queryLimits | indent 4 }} 78 | {{- end }} 79 | -------------------------------------------------------------------------------- /deploy/charts/mysql-cluster/templates/secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if not .Values.customSecretName }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ include "mysql-cluster.secretName" . }} 6 | labels: 7 | app: {{ template "mysql-cluster.name" . }} 8 | chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} 9 | release: {{ .Release.Name }} 10 | heritage: {{ .Release.Service }} 11 | {{- if .Values.appSecretLabels }} 12 | {{- toYaml .Values.appSecretLabels | nindent 4 }} 13 | {{- end }} 14 | {{- if .Values.appSecretAnnotations }} 15 | annotations: 16 | {{ toYaml .Values.appSecretAnnotations }} 17 | {{- end }} 18 | type: Opaque 19 | data: 20 | ROOT_PASSWORD: {{ required ".rootPassword is missing" .Values.rootPassword | b64enc | quote }} 21 | USER: {{ .Values.appUser | b64enc | quote }} 22 | PASSWORD: {{ .Values.appPassword | b64enc | quote }} 23 | DATABASE: {{ .Values.appDatabase | b64enc | quote }} 24 | DB_CONNECT_URL: {{ include "mysql-cluster.dbConnectURL" . | b64enc | quote }} 25 | {{- end }} 26 | -------------------------------------------------------------------------------- /deploy/charts/mysql-cluster/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for mysql-cluster. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | ## The cluster number of nodes 6 | replicas: 1 7 | 8 | ## For setting custom docker image or specifying mysql version 9 | ## the image field has priority over mysqlVersion. 10 | # image: percona:5.7 11 | # mysqlVersion: "5.7" 12 | 13 | ## MySQL connect credentials, those credentials will be provisioned in the cluster 14 | rootPassword: "CHANGE_ME" 15 | appUser: "" 16 | appPassword: "" 17 | appDatabase: "" 18 | # appSecretLabels: {} 19 | # appSecretAnnotations: {} 20 | 21 | podSpec: 22 | mysqlConf: 23 | volumeSpec: 24 | 25 | serverIDOffset: 26 | 27 | initBucketURL: 28 | initBucketSecretName: 29 | 30 | backupSchedule: 31 | backupScheduleJobsHistoryLimit: 32 | backupURL: 33 | backupSecretName: 34 | backupRemoteDeletePolicy: 35 | # backupSecretLabels: {} 36 | # backupSecretAnnotations: {} 37 | backupCredentials: 38 | # use s3 https://rclone.org/s3/ 39 | # S3_PROVIDER: ? # like: AWS, Minio, Ceph, and so on 40 | # S3_ENDPOINT: ? 41 | # AWS_ACCESS_KEY_ID: ? 42 | # AWS_SECRET_ACCESS_KEY: ? 43 | # AWS_REGION: ? 44 | # AWS_ACL: ? 45 | # AWS_STORAGE_CLASS: ? 46 | # AWS_SESSION_TOKEN: ? 47 | 48 | # use google cloud storage https://rclone.org/googlecloudstorage/ 49 | # GCS_SERVICE_ACCOUNT_JSON_KEY: ? 50 | # GCS_PROJECT_ID: ? 51 | # GCS_OBJECT_ACL: ? 52 | # GCS_BUCKET_ACL: ? 53 | # GCS_LOCATION: ? 54 | # GCS_STORAGE_CLASS: MULTI_REGIONAL 55 | 56 | # use http https://rclone.org/http/ 57 | # HTTP_URL: ? 58 | 59 | # use google drive https://rclone.org/drive/ 60 | # GDRIVE_CLIENT_ID: ? 61 | # GDRIVE_ROOT_FOLDER_ID: ? 62 | # GDRIVE_IMPERSONATOR: ? 63 | 64 | # use azure https://rclone.org/azureblob/ 65 | # AZUREBLOB_ACCOUNT: ? 66 | # AZUREBLOB_KEY: ? 67 | 68 | ## For enabling and configuring pt-kill: https://www.percona.com/doc/percona-toolkit/LATEST/pt-kill.html 69 | #queryLimits: 70 | # maxIdleTime: ... # pt-kill --idle-time 71 | # maxQueryTime: ... # pt-kill --busy-time 72 | # kill: oldest|all|all-but-oldest # pt-kill --victims 73 | # killMode: query|connection # pt-kill --kill-query or pt-kill --kill 74 | # ignoreDb: [] # pt-kill --ignore-db ... 75 | # ignoreCommand: [] # pt-kill --ignore-command ... 76 | # ignoreUser: [] # pt-kill --ignore-user 77 | -------------------------------------------------------------------------------- /deploy/charts/mysql-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 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | name: mysql-operator 3 | description: A helm chart for Bitpoke Operator for MySQL 4 | appVersion: "latest" 5 | kubeVersion: ">= 1.19.0-0" 6 | keywords: 7 | - mysql 8 | - percona 9 | - orchestrator 10 | - bitpoke 11 | - database 12 | version: 0.0.0 13 | home: https://www.bitpoke.io/mysql-operator/ 14 | sources: 15 | - https://github.com/bitpoke/mysql-operator.git 16 | annotations: 17 | artifacthub.io/license: "Apache-2.0" 18 | artifacthub.io/operator: "true" 19 | artifacthub.io/operatorCapabilities: "full lifecycle" 20 | artifacthub.io/images: | 21 | - name: mysql-operator 22 | image: docker.io/bitpoke/mysql-operator:latest 23 | whitelisted: true 24 | - name: orchestrator 25 | image: docker.io/bitpoke/mysql-operator-orchestrator:latest 26 | whitelisted: true 27 | - name: mysql-5.7-sidecar 28 | image: docker.io/bitpoke/mysql-operator-sidecar-5.7:latest 29 | whitelisted: true 30 | - name: mysql-8.0-sidecar 31 | image: docker.io/bitpoke/mysql-operator-sidecar-8.0:latest 32 | whitelisted: true 33 | artifacthub.io/crds: | 34 | - kind: MySQLCluster 35 | version: v1alpha1 36 | name: mysqlclusters.mysql.presslabs.org 37 | displayName: MySQLCluster 38 | description: Represents a MySQL cluster managed by Bitpoke's Operator for MySQL. 39 | artifacthub.io/crdsExamples: | 40 | - apiVersion: v1alpha1 41 | kind: MysqlCluster 42 | metadata: 43 | name: my-cluster 44 | spec: 45 | replicas: 3 46 | secretName: my-secret 47 | mysqlConf: 48 | innodb-buffer-size: 128M 49 | targetSLO: 50 | maxSlaveLatency: 10s 51 | 52 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/templates/NOTES.txt: -------------------------------------------------------------------------------- 1 | You can create a new cluster by issuing: 2 | 3 | cat <=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} 5 | {{- if not (hasKey .Values.orchestrator.ingress.annotations "kubernetes.io/ingress.class") }} 6 | {{- $_ := set .Values.orchestrator.ingress.annotations "kubernetes.io/ingress.class" .Values.orchestrator.ingress.className}} 7 | {{- end }} 8 | {{- end }} 9 | {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} 10 | apiVersion: networking.k8s.io/v1 11 | {{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} 12 | apiVersion: networking.k8s.io/v1beta1 13 | {{- else -}} 14 | apiVersion: extensions/v1beta1 15 | {{- end }} 16 | kind: Ingress 17 | metadata: 18 | name: {{ $fullName }} 19 | labels: 20 | {{- include "mysql-operator.labels" . | nindent 4 }} 21 | {{- with .Values.orchestrator.ingress.annotations }} 22 | annotations: 23 | {{- toYaml . | nindent 4 }} 24 | {{- end }} 25 | spec: 26 | {{- if and .Values.orchestrator.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} 27 | ingressClassName: {{ .Values.orchestrator.ingress.className }} 28 | {{- end }} 29 | {{- if .Values.orchestrator.ingress.tls }} 30 | tls: 31 | {{- range .Values.orchestrator.ingress.tls }} 32 | - hosts: 33 | {{- range .hosts }} 34 | - {{ . | quote }} 35 | {{- end }} 36 | secretName: {{ .secretName }} 37 | {{- end }} 38 | {{- end }} 39 | rules: 40 | {{- range .Values.orchestrator.ingress.hosts }} 41 | - host: {{ .host | quote }} 42 | http: 43 | paths: 44 | {{- range .paths }} 45 | - path: {{ .path }} 46 | {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} 47 | pathType: {{ .pathType }} 48 | {{- end }} 49 | backend: 50 | {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} 51 | service: 52 | name: {{ $fullName }} 53 | port: 54 | number: {{ $svcPort }} 55 | {{- else }} 56 | serviceName: {{ $fullName }} 57 | servicePort: {{ $svcPort }} 58 | {{- end }} 59 | {{- end }} 60 | {{- end }} 61 | {{- end }} 62 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/templates/orchestrator-raft-service.yaml: -------------------------------------------------------------------------------- 1 | {{- $replicas := int .Values.replicaCount -}} 2 | {{- $labels := (include "mysql-operator.labels" .) -}} 3 | {{- $fullName := (include "mysql-operator.fullname" .) -}} 4 | {{- range $i := until $replicas -}} 5 | apiVersion: v1 6 | kind: Service 7 | metadata: 8 | name: {{ printf "%s-%d-orc-svc" $fullName $i }} 9 | labels: 10 | app.kubernetes.io/component: orchestrator-raft 11 | {{- $labels | nindent 4}} 12 | spec: 13 | type: ClusterIP 14 | publishNotReadyAddresses: true 15 | ports: 16 | - name: http 17 | port: 80 18 | targetPort: 3000 19 | - name: raft 20 | port: 10008 21 | targetPort: 10008 22 | selector: 23 | statefulset.kubernetes.io/pod-name: {{ printf "%s-%d" $fullName $i }} 24 | --- 25 | {{end}} 26 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/templates/orchestrator-secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if not .Values.orchestrator.secretName }} 2 | apiVersion: v1 3 | kind: Secret 4 | metadata: 5 | name: {{ template "orchestrator.fullname" . }} 6 | labels: 7 | {{- include "mysql-operator.labels" . | nindent 4 }} 8 | data: 9 | TOPOLOGY_USER: {{ printf "%s" .Values.orchestrator.topologyUser | b64enc | quote }} 10 | {{- if .Values.orchestrator.topologyPassword }} 11 | TOPOLOGY_PASSWORD: {{ printf "%s" .Values.orchestrator.topologyPassword | b64enc | quote }} 12 | {{- else }} 13 | TOPOLOGY_PASSWORD: {{ randAlphaNum 10 | b64enc | quote }} 14 | {{- end }} 15 | {{- end }} 16 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/templates/pdb.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.podDisruptionBudget.enabled (gt (int64 .Values.replicaCount) 1) }} 2 | apiVersion: policy/v1 3 | kind: PodDisruptionBudget 4 | metadata: 5 | name: {{ include "mysql-operator.fullname" . }} 6 | labels: 7 | {{- include "mysql-operator.labels" . | nindent 4 }} 8 | spec: 9 | {{- if .Values.podDisruptionBudget.minAvailable }} 10 | minAvailable: {{ .Values.podDisruptionBudget.minAvailable }} 11 | {{- end }} 12 | {{- if .Values.podDisruptionBudget.maxUnavailable }} 13 | maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }} 14 | {{- end }} 15 | selector: 16 | matchLabels: 17 | {{- include "mysql-operator.selectorLabels" . | nindent 6 }} 18 | {{- end }} 19 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/templates/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: {{ include "mysql-operator.fullname" . }} 5 | labels: 6 | {{- include "mysql-operator.labels" . | nindent 4 }} 7 | app.kubernetes.io/component: operator 8 | spec: 9 | type: {{ .Values.orchestrator.service.type }} 10 | ports: 11 | - port: {{ .Values.orchestrator.service.port }} 12 | name: http 13 | protocol: TCP 14 | targetPort: http 15 | {{- if .Values.orchestrator.service.nodePort }} 16 | nodePort: {{ .Values.orchestrator.service.nodePort }} 17 | {{- end }} 18 | - port: {{ .Values.serviceMonitor.servicePort }} 19 | name: {{ .Values.serviceMonitor.servicePortName }} 20 | protocol: TCP 21 | targetPort: prometheus 22 | selector: 23 | {{- include "mysql-operator.selectorLabels" . | nindent 4 }} 24 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/templates/serviceaccount.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.serviceAccount.create -}} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | name: {{ include "mysql-operator.serviceAccountName" . }} 6 | labels: 7 | {{- include "mysql-operator.labels" . | nindent 4 }} 8 | {{- with .Values.serviceAccount.annotations }} 9 | annotations: 10 | {{- toYaml . | nindent 4 }} 11 | {{- end }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /deploy/charts/mysql-operator/templates/servicemonitor.yaml: -------------------------------------------------------------------------------- 1 | # This is a ServicMonitor for the MySQL operator itself. 2 | # To scrape the operator, we need https://github.com/bitpoke/mysql-operator/issues/151 first. 3 | {{- if .Values.serviceMonitor.enabled }} 4 | {{- if .Capabilities.APIVersions.Has "monitoring.coreos.com/v1" }} 5 | apiVersion: monitoring.coreos.com/v1 6 | kind: ServiceMonitor 7 | metadata: 8 | name: {{ template "mysql-operator.fullname" . }} 9 | labels: 10 | {{- include "mysql-operator.labels" . | nindent 4 }} 11 | {{- if .Values.serviceMonitor.additionalLabels }} 12 | {{ toYaml .Values.serviceMonitor.additionalLabels | indent 4 }} 13 | {{- end }} 14 | spec: 15 | selector: 16 | matchLabels: 17 | {{- include "mysql-operator.selectorLabels" . | nindent 6 }} 18 | app.kubernetes.io/component: operator 19 | {{- with .Values.serviceMonitor.jobLabel }} 20 | jobLabel: {{ . | quote}} 21 | {{- end }} 22 | {{- with .Values.serviceMonitor.targetLabels }} 23 | targetLabels: 24 | {{ toYaml . | trim | indent 4 -}} 25 | {{- end }} 26 | {{- with .Values.serviceMonitor.podTargetLabels }} 27 | podTargetLabels: 28 | {{ toYaml . | trim | indent 4 -}} 29 | {{- end }} 30 | endpoints: 31 | - path: /metrics 32 | port: {{ .Values.serviceMonitor.servicePortName }} 33 | {{- if .Values.serviceMonitor.interval }} 34 | interval: {{ .Values.serviceMonitor.interval }} 35 | {{- end }} 36 | {{- if .Values.serviceMonitor.scrapeTimeout }} 37 | scrapeTimeout: {{ .Values.serviceMonitor.scrapeTimeout }} 38 | {{- end }} 39 | {{- if .Values.serviceMonitor.metricRelabelings }} 40 | metricRelabelings: {{ toYaml .Values.serviceMonitor.metricRelabelings | nindent 8 }} 41 | {{- end }} 42 | {{- end }} 43 | {{- end }} 44 | -------------------------------------------------------------------------------- /examples/example-backup-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: my-cluster-backup-secret 5 | type: Opaque 6 | data: 7 | # AWS_ACCESS_KEY_ID: # 8 | # AWS_SECRET_ACCESS_KEY: # 9 | # AWS_REGION: us-east-1 10 | # AWS_ACL: ? 11 | # S3_PROVIDER: AWS 12 | # S3_ENDPOINT: ? 13 | 14 | # GCS_SERVICE_ACCOUNT_JSON_KEY: ? 15 | # GCS_PROJECT_ID: ? 16 | # GCS_OBJECT_ACL: ? 17 | # GCS_BUCKET_ACL: ? 18 | # GCS_LOCATION: ? 19 | # GCS_STORAGE_CLASS: MULTI_REGIONAL 20 | 21 | # only for read-only purpose 22 | # HTTP_URL: ? 23 | 24 | # AZUREBLOB_ACCOUNT: ? 25 | # AZUREBLOB_KEY: ? 26 | 27 | # HDFS_NAMENODE: ? 28 | # HDFS_USERNAME: ? 29 | 30 | # for more details check docker entrypoint: hack/docker/mysql-helper/docker-entrypoint.sh 31 | # and rclone documentation: https://rclone.org/ 32 | -------------------------------------------------------------------------------- /examples/example-backup.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlBackup 3 | metadata: 4 | name: my-cluster-backup 5 | 6 | spec: 7 | # this field is required 8 | clusterName: my-cluster 9 | 10 | ## if backupURL is specified then the backup will be put 11 | ## at this path, else the backup URL will be filled with 12 | ## the cluster preset backupURL and a random name 13 | # backupURL: gs://bucket_name/path/to/backup.xtrabackup.gz 14 | # backupURL: hdfs://bucket_name/path/to/backup.xtrabackup.gz 15 | 16 | ## specify a secret where to find credentials to access the 17 | ## bucket 18 | # backupSecretName: backup-secret 19 | 20 | ## specify the remote deletion policy. It can be on of ["retain", "delete"] 21 | # remoteDeletePolicy: retain 22 | -------------------------------------------------------------------------------- /examples/example-cluster-init.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlCluster 3 | metadata: 4 | name: foob2 5 | spec: 6 | replicas: 2 7 | secretName: the-secret 8 | 9 | backupSchedule: "0 1 1 * * *" 10 | backupURL: gs://bucket_name/path/ 11 | backupSecretName: backup-secret 12 | 13 | initBucketURL: gs://bucket_name/path/to/backup.xbackup.gz 14 | initBucketSecretName: backup-secret 15 | -------------------------------------------------------------------------------- /examples/example-cluster-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: my-secret 5 | type: Opaque 6 | data: 7 | # root password is required to be specified 8 | ROOT_PASSWORD: bXlwYXNz 9 | ## application credentials that will be created at cluster bootstrap 10 | # DATABASE: 11 | # USER: 12 | # PASSWORD: 13 | -------------------------------------------------------------------------------- /examples/example-database.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: mysql.presslabs.org/v1alpha1 2 | kind: MysqlDatabase 3 | metadata: 4 | name: my-database 5 | # annotations: 6 | # mysql-operator.presslabs.org/resourceDeletionPolicy: retain # When the MysqlDatabase is deleted, the MySQL DB will be preserved 7 | spec: 8 | database: db-name-in-mysql 9 | clusterRef: 10 | name: my-cluster 11 | namespace: default 12 | -------------------------------------------------------------------------------- /examples/example-user.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Secret 3 | metadata: 4 | name: my-user-password 5 | data: 6 | PASSWORD: bXlzcWwtcGFzc3dvcmQtZm9yLXVzZXI= 7 | 8 | --- 9 | apiVersion: mysql.presslabs.org/v1alpha1 10 | kind: MysqlUser 11 | metadata: 12 | name: my-user 13 | # annotations: 14 | # mysql-operator.presslabs.org/resourceDeletionPolicy: retain # When the MysqlUser is deleted, the MySQL user will be preserved 15 | spec: 16 | user: user-name-in-mysql 17 | clusterRef: 18 | name: my-cluster 19 | namespace: default 20 | password: 21 | name: my-user-password 22 | key: PASSWORD 23 | allowedHosts: 24 | - localhost 25 | permissions: 26 | - schema: db-name-in-mysql 27 | tables: ["table1", "table2"] 28 | permissions: 29 | - SELECT 30 | - UPDATE 31 | - CREATE 32 | -------------------------------------------------------------------------------- /hack/02x-crds.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.k8s.io/v1 2 | kind: CustomResourceDefinition 3 | metadata: 4 | name: mysqlbackups.mysql.presslabs.org 5 | annotations: 6 | helm.sh/hook: crd-install 7 | spec: 8 | group: mysql.presslabs.org 9 | names: 10 | kind: MysqlBackup 11 | plural: mysqlbackups 12 | scope: Namespaced 13 | version: v1alpha1 14 | 15 | --- 16 | apiVersion: apiextensions.k8s.io/v1 17 | kind: CustomResourceDefinition 18 | metadata: 19 | name: mysqlclusters.mysql.presslabs.org 20 | annotations: 21 | helm.sh/hook: crd-install 22 | spec: 23 | group: mysql.presslabs.org 24 | names: 25 | kind: MysqlCluster 26 | plural: mysqlclusters 27 | shortNames: 28 | - mysql 29 | scope: Namespaced 30 | subresources: 31 | scale: 32 | specReplicasPath: .spec.replicas 33 | statusReplicasPath: .status.readyNodes 34 | status: {} 35 | version: v1alpha1 36 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | -------------------------------------------------------------------------------- /hack/chart-metadata.yaml: -------------------------------------------------------------------------------- 1 | metadata: 2 | name: '{{ template "mysql-operator.fullname" . }}' 3 | labels: 4 | app: '{{ template "mysql-operator.name" . }}' 5 | chart: '{{ template "mysql-operator.chart" . }}' 6 | release: '{{ .Release.Name }}' 7 | heritage: '{{ .Release.Service }}' 8 | -------------------------------------------------------------------------------- /hack/development/Dockerfile.operator: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | 3 | # set expiration time for dev images 4 | # https://support.coreos.com/hc/en-us/articles/115001384693-Tag-Expiration 5 | LABEL quay.expires-after=2d 6 | 7 | COPY ./bin/mysql-operator_linux_amd64 /mysql-operator 8 | ENTRYPOINT ["/mysql-operator"] 9 | -------------------------------------------------------------------------------- /hack/development/Dockerfile.orchestrator: -------------------------------------------------------------------------------- 1 | ############################################################################## 2 | # Build Orchestrator 3 | ############################################################################### 4 | FROM golang:1.14.10-alpine3.12 as builder-orc 5 | 6 | RUN set -ex \ 7 | && apk add --no-cache \ 8 | bash gcc git musl-dev openssl rsync perl-utils 9 | 10 | ARG ORCHESTRATOR_VERSION=v3.2.3 11 | ARG ORCHESTRATOR_REPO=https://github.com/openark/orchestrator.git 12 | RUN set -ex \ 13 | && mkdir -p $GOPATH/src/github.com/openark/orchestrator \ 14 | && cd $GOPATH/src/github.com/openark/orchestrator \ 15 | && git init && git remote add origin $ORCHESTRATOR_REPO \ 16 | && git fetch --tags \ 17 | && git checkout $ORCHESTRATOR_VERSION 18 | 19 | WORKDIR $GOPATH/src/github.com/openark/orchestrator 20 | 21 | RUN set -ex && ./build.sh -b -P 22 | 23 | 24 | ############################################################################### 25 | # Docker image for orchestrator 26 | ############################################################################### 27 | FROM alpine:3.12 28 | 29 | # Create a group and user 30 | RUN addgroup -g 777 orchestrator && adduser -u 777 -g 777 -S orchestrator 31 | 32 | ENV DOCKERIZE_VERSION v0.6.1 33 | RUN set -ex \ 34 | && apk add --update --no-cache \ 35 | curl \ 36 | wget \ 37 | tar \ 38 | openssl \ 39 | && mkdir /etc/orchestrator /var/lib/orchestrator \ 40 | && chown -R 777:777 /etc/orchestrator /var/lib/orchestrator \ 41 | && wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-alpine-linux-amd64-$DOCKERIZE_VERSION.tar.gz -O- | \ 42 | tar -C /usr/local/bin -xzv 43 | 44 | COPY --chown=777:777 hack/docker/orchestrator/ / 45 | COPY --from=builder-orc /tmp/orchestrator-release/build/orchestrator/usr/local/orchestrator/ /usr/local/orchestrator/ 46 | COPY ./bin/orc-helper_linux_amd64 /usr/local/bin/orc-helper 47 | 48 | USER 777 49 | EXPOSE 3000 10008 50 | VOLUME [ "/var/lib/orchestrator" ] 51 | 52 | ENTRYPOINT ["/usr/local/bin/docker-entrypoint"] 53 | CMD ["/usr/local/bin/orchestrator", "-config", "/etc/orchestrator/orchestrator.conf.json", "http"] 54 | 55 | # set expiration time for dev images 56 | # https://support.coreos.com/hc/en-us/articles/115001384693-Tag-Expiration 57 | LABEL quay.expires-after=2d 58 | -------------------------------------------------------------------------------- /hack/development/Dockerfile.sidecar: -------------------------------------------------------------------------------- 1 | # NOTE: this image is for development only 2 | # Copy the mysql-operator-sidecar into its own image 3 | 4 | 5 | ############################################################################### 6 | # Build rclone 7 | ############################################################################### 8 | 9 | FROM debian:stretch as rclone 10 | 11 | RUN apt-get update \ 12 | && apt-get install -y --no-install-recommends \ 13 | gnupg ca-certificates wget unzip 14 | 15 | COPY hack/docker/rclone.gpg /root/rclone.gpg 16 | RUN gpg --import /root/rclone.gpg 17 | 18 | ENV RCLONE_VERSION=1.57.0 19 | 20 | RUN wget -nv https://github.com/ncw/rclone/releases/download/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-amd64.zip \ 21 | && wget -nv https://github.com/ncw/rclone/releases/download/v${RCLONE_VERSION}/SHA256SUMS \ 22 | && gpg --verify --output=- SHA256SUMS > sums \ 23 | && sha256sum -c --ignore-missing sums \ 24 | && unzip rclone-*-linux-amd64.zip \ 25 | && mv rclone-*-linux-amd64/rclone /usr/local/bin/ \ 26 | && chmod 755 /usr/local/bin/rclone 27 | 28 | 29 | ############################################################################### 30 | # Docker image for sidecar containers 31 | ############################################################################### 32 | 33 | FROM debian:buster-slim as sidecar 34 | 35 | RUN groupadd -g 999 mysql 36 | RUN useradd -u 999 -r -g 999 -s /sbin/nologin \ 37 | -c "Default Application User" mysql 38 | 39 | RUN apt-get update \ 40 | && apt-get install -y --no-install-recommends \ 41 | apt-transport-https ca-certificates pigz wget \ 42 | && rm -rf /var/lib/apt/lists/* 43 | 44 | COPY hack/docker/percona.gpg /etc/apt/trusted.gpg.d/percona.gpg 45 | RUN echo 'deb https://repo.percona.com/apt buster main' > /etc/apt/sources.list.d/percona.list 46 | 47 | ARG XTRABACKUP_PKG=percona-xtrabackup-80 48 | RUN apt-get update \ 49 | && apt-get install -y --no-install-recommends \ 50 | percona-toolkit ${XTRABACKUP_PKG} unzip default-mysql-client 51 | 52 | USER mysql 53 | 54 | # set expiration time for dev images 55 | # https://support.coreos.com/hc/en-us/articles/115001384693-Tag-Expiration 56 | LABEL quay.expires-after=2d 57 | 58 | COPY --from=rclone /usr/local/bin/rclone /usr/local/bin/rclone 59 | COPY ./hack/docker/sidecar-entrypoint.sh /usr/local/bin/sidecar-entrypoint.sh 60 | COPY ./bin/mysql-operator-sidecar_linux_amd64 /usr/local/bin/mysql-operator-sidecar 61 | 62 | ENTRYPOINT ["/usr/local/bin/sidecar-entrypoint.sh"] 63 | -------------------------------------------------------------------------------- /hack/development/dev-values.yaml: -------------------------------------------------------------------------------- 1 | extraArgs: 2 | - --debug 3 | 4 | installCRDs: false 5 | 6 | orchestrator: 7 | topologyPassword: password1 8 | antiAffinity: soft 9 | replicas: 1 10 | -------------------------------------------------------------------------------- /hack/development/related-go-files.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # This command outputs a list of go files on which the given go file depends on 3 | # Example: related-go-files.sh 4 | 5 | set -e 6 | 7 | go list -f '{{ join .Deps "\n" }}' $2 | grep 'pkg' | grep -v 'vendor' | sed -e "s|^$1/||" | xargs -I % find % -name "*.go" -maxdepth 1 -type f 2>/dev/null 8 | -------------------------------------------------------------------------------- /hack/generate_chart.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | tag="$1" 4 | if [ -z "$tag" ] ; then 5 | echo "Usage: $0 " >&2 6 | exit 1 7 | fi 8 | version="${tag#v}" 9 | 10 | CHART_PATH=../charts/mysql-operator 11 | 12 | echo "Updating chart to version to: ${version}" 13 | sed -i.bak -E " 14 | s#version: .*#version: ${version}# 15 | s#appVersion: .*#appVersion: ${tag}# 16 | " ${CHART_PATH}/Chart.yaml 17 | rm ${CHART_PATH}/Chart.yaml.bak 18 | 19 | echo "Updating chart images tag to: ${version}" 20 | sed -i.bak -E " 21 | s#image: (.*):.*#image: \\1:${version}# 22 | s#sidecarImage: (.*):.*#sidecarImage: \\1:${version}# 23 | s#sidecarMysql8Image: (.*):.*#sidecarMysql8Image: \\1:${version}# 24 | s# image: (.*): (.*):.*# image: \\1:${version}# 25 | " ${CHART_PATH}/values.yaml 26 | rm ${CHART_PATH}/values.yaml.bak 27 | -------------------------------------------------------------------------------- /hack/license-check: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | MISSING=($(grep -s -rL --include "*.go" "Licensed under the Apache License, Version 2.0" pkg cmd)) 3 | 4 | if [[ -n "${MISSING[0]}" ]]; then 5 | echo "Files missing license header:" 6 | printf "\t%s\n" "${MISSING[@]}" 7 | exit 1 8 | fi 9 | exit 0 10 | -------------------------------------------------------------------------------- /images/mysql-operator-orchestrator/Dockerfile: -------------------------------------------------------------------------------- 1 | # Docker image for orchestrator 2 | # The base image is pinned to the debug-nonroot tag 3 | FROM gcr.io/distroless/base-debian11@sha256:e76722f06f7c15e0076072fb02782ec59923b0d658b8a3d80bb79deaee6fb44d 4 | SHELL ["/busybox/sh", "-c"] 5 | 6 | # switch to root for installing software 7 | USER root 8 | 9 | RUN set -ex \ 10 | && mkdir -p /usr/local/bin \ 11 | && export DOCKERIZE_VERSION=0.6.1 \ 12 | && wget https://github.com/jwilder/dockerize/releases/download/v${DOCKERIZE_VERSION}/dockerize-linux-amd64-v${DOCKERIZE_VERSION}.tar.gz -O- | \ 13 | tar -C /usr/local/bin -xzv 14 | 15 | RUN set -ex \ 16 | && export ORCHESTRATOR_VERSION=3.2.6 \ 17 | && wget https://github.com/openark/orchestrator/releases/download/v${ORCHESTRATOR_VERSION}/orchestrator-${ORCHESTRATOR_VERSION}-linux-amd64.tar.gz -O- | \ 18 | tar -C / -xzv 19 | 20 | COPY rootfs/ / 21 | RUN set -ex \ 22 | && mkdir -p /etc/orchestrator /var/lib/orchestrator \ 23 | && chown -R 65532:65532 /etc/orchestrator /var/lib/orchestrator 24 | 25 | # switch back to nonroot for runtime 26 | USER 65532 27 | EXPOSE 3000 10008 28 | VOLUME [ "/var/lib/orchestrator" ] 29 | 30 | WORKDIR "/usr/local/orchestrator" 31 | 32 | ENTRYPOINT [ "/usr/local/bin/dockerize", \ 33 | "-no-overwrite", \ 34 | "-template", \ 35 | "/usr/local/share/orchestrator/templates/orchestrator.conf.json:/etc/orchestrator/orchestrator.conf.json", \ 36 | "-template", \ 37 | "/usr/local/share/orchestrator/templates/orc-topology.cnf:/etc/orchestrator/orc-topology.cnf", \ 38 | "--" ] 39 | CMD ["/usr/local/orchestrator/orchestrator", "-config", "/etc/orchestrator/orchestrator.conf.json", "http"] 40 | -------------------------------------------------------------------------------- /images/mysql-operator-orchestrator/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORMS := linux_amd64 2 | include ../../build/makelib/common.mk 3 | 4 | IMAGE = $(BUILD_REGISTRY)/mysql-operator-orchestrator-$(ARCH) 5 | CACHE_IMAGES = $(IMAGE) 6 | include ../../build/makelib/image.mk 7 | 8 | img.build: 9 | @$(INFO) docker build $(IMAGE) $(IMAGE_PLATFORM) 10 | @cp -La . $(IMAGE_TEMP_DIR) 11 | @mkdir -p $(IMAGE_TEMP_DIR)/rootfs/usr/local/bin 12 | @cp $(OUTPUT_DIR)/bin/linux_$(ARCH)/orc-helper $(IMAGE_TEMP_DIR)/rootfs/usr/local/bin/orc-helper 13 | @docker buildx build $(BUILD_ARGS) \ 14 | --platform $(IMAGE_PLATFORM) \ 15 | -t $(IMAGE) \ 16 | --build-arg ARCH=$(ARCH) \ 17 | $(IMAGE_TEMP_DIR) 18 | @$(OK) docker build $(IMAGE) 19 | -------------------------------------------------------------------------------- /images/mysql-operator-orchestrator/rootfs/usr/local/share/orchestrator/templates/orc-topology.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | user = {{ .Env.ORC_TOPOLOGY_USER }} 3 | password = {{ .Env.ORC_TOPOLOGY_PASSWORD }} 4 | -------------------------------------------------------------------------------- /images/mysql-operator-orchestrator/rootfs/usr/local/share/orchestrator/templates/orchestrator.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "Debug": {{ default .Env.ORC_DEBUG "false" | lower }}, 3 | "ListenAddress": ":3000", 4 | "BackendDB": "sqlite", 5 | "SQLite3DataFile": "/var/lib/orchestrator/orc.db", 6 | "MySQLTopologyCredentialsConfigFile": "/etc/orchestrator/orc-topology.cnf" 7 | } 8 | -------------------------------------------------------------------------------- /images/mysql-operator-sidecar-5.7/Dockerfile: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Docker image for sidecar containers 3 | ############################################################################### 4 | 5 | FROM debian:buster-slim as sidecar 6 | 7 | RUN groupadd -g 999 mysql 8 | RUN useradd -u 999 -r -g 999 -s /sbin/nologin \ 9 | -c "Default Application User" mysql 10 | 11 | COPY rootfs/ / 12 | 13 | RUN apt-get update \ 14 | && apt-get install -y --no-install-recommends \ 15 | apt-transport-https ca-certificates unzip pigz wget gnupg \ 16 | && rm -rf /var/lib/apt/lists/* 17 | 18 | RUN export RCLONE_VERSION=1.57.0 \ 19 | && gpg --import /etc/gpg-keys/rclone.gpg \ 20 | && wget -nv https://github.com/ncw/rclone/releases/download/v${RCLONE_VERSION}/rclone-v${RCLONE_VERSION}-linux-amd64.zip \ 21 | && wget -nv https://github.com/ncw/rclone/releases/download/v${RCLONE_VERSION}/SHA256SUMS \ 22 | && gpg --verify --output=- SHA256SUMS > sums \ 23 | && sha256sum -c --ignore-missing sums \ 24 | && unzip rclone-*-linux-amd64.zip \ 25 | && mv rclone-*-linux-amd64/rclone /usr/local/bin/ \ 26 | && chmod 755 /usr/local/bin/rclone \ 27 | && rm -r rclone-*-linux-amd64 rclone-*-linux-amd64.zip 28 | 29 | RUN echo 'deb https://repo.percona.com/apt buster main' > /etc/apt/sources.list.d/percona.list 30 | 31 | ARG XTRABACKUP_PKG=percona-xtrabackup-24 32 | RUN apt-get update \ 33 | && apt-get install -y --no-install-recommends \ 34 | percona-toolkit ${XTRABACKUP_PKG} unzip default-mysql-client \ 35 | && rm -rf /var/lib/apt/lists/* 36 | 37 | USER mysql 38 | ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] 39 | -------------------------------------------------------------------------------- /images/mysql-operator-sidecar-5.7/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORMS := linux_amd64 2 | include ../../build/makelib/common.mk 3 | 4 | IMAGE ?= $(BUILD_REGISTRY)/mysql-operator-sidecar-5.7-$(ARCH) 5 | CACHE_IMAGES = $(IMAGE) 6 | include ../../build/makelib/image.mk 7 | 8 | img.build: 9 | @$(INFO) docker build $(IMAGE) $(IMAGE_PLATFORM) 10 | @cp -La . $(IMAGE_TEMP_DIR) 11 | @cp $(OUTPUT_DIR)/bin/linux_$(ARCH)/mysql-operator-sidecar $(IMAGE_TEMP_DIR)/rootfs/usr/local/bin/mysql-operator-sidecar 12 | @docker buildx build $(BUILD_ARGS) \ 13 | --platform $(IMAGE_PLATFORM) \ 14 | -t $(IMAGE) \ 15 | --build-arg ARCH=$(ARCH) \ 16 | $(IMAGE_TEMP_DIR) 17 | @$(OK) docker build $(IMAGE) 18 | -------------------------------------------------------------------------------- /images/mysql-operator-sidecar-5.7/rootfs/etc/apt/trusted.gpg.d/percona.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpoke/mysql-operator/37c64109855691ab227130cf2d011effdd48690b/images/mysql-operator-sidecar-5.7/rootfs/etc/apt/trusted.gpg.d/percona.gpg -------------------------------------------------------------------------------- /images/mysql-operator-sidecar-5.7/rootfs/etc/gpg-keys/rclone.gpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bitpoke/mysql-operator/37c64109855691ab227130cf2d011effdd48690b/images/mysql-operator-sidecar-5.7/rootfs/etc/gpg-keys/rclone.gpg -------------------------------------------------------------------------------- /images/mysql-operator-sidecar-5.7/rootfs/usr/local/bin/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | echo "Create Google Drive service-account.json file." 5 | echo "${GDRIVE_SERVICE_ACCOUNT}" > /tmp/gdrive-service-account.json 6 | 7 | echo "Create rclone.conf file." 8 | cat < /tmp/rclone.conf 9 | [gd] 10 | type = drive 11 | scope = drive 12 | service_account_file = /tmp/gdrive-service-account.json 13 | client_id = ${GDRIVE_CLIENT_ID} 14 | root_folder_id = ${GDRIVE_ROOT_FOLDER_ID} 15 | impersonate = ${GDRIVE_IMPERSONATOR} 16 | 17 | [s3] 18 | type = s3 19 | env_auth = true 20 | provider = ${S3_PROVIDER:-"AWS"} 21 | access_key_id = ${AWS_ACCESS_KEY_ID} 22 | secret_access_key = ${AWS_SECRET_ACCESS_KEY:-$AWS_SECRET_KEY} 23 | region = ${AWS_REGION:-"us-east-1"} 24 | endpoint = ${S3_ENDPOINT} 25 | acl = ${AWS_ACL} 26 | storage_class = ${AWS_STORAGE_CLASS} 27 | session_token = ${AWS_SESSION_TOKEN} 28 | no_check_bucket = true 29 | 30 | [gs] 31 | type = google cloud storage 32 | project_number = ${GCS_PROJECT_ID} 33 | service_account_file = /tmp/google-credentials.json 34 | object_acl = ${GCS_OBJECT_ACL} 35 | bucket_acl = ${GCS_BUCKET_ACL} 36 | location = ${GCS_LOCATION} 37 | storage_class = ${GCS_STORAGE_CLASS:-"MULTI_REGIONAL"} 38 | 39 | [http] 40 | type = http 41 | url = ${HTTP_URL} 42 | 43 | [azure] 44 | type = azureblob 45 | account = ${AZUREBLOB_ACCOUNT} 46 | key = ${AZUREBLOB_KEY} 47 | 48 | [hdfs] 49 | type = hdfs 50 | namenode = ${HDFS_NAMENODE} 51 | username = ${HDFS_USERNAME} 52 | EOF 53 | 54 | if [[ -n "${GCS_SERVICE_ACCOUNT_JSON_KEY:-}" ]]; then 55 | echo "Create google-credentials.json file." 56 | cat < /tmp/google-credentials.json 57 | ${GCS_SERVICE_ACCOUNT_JSON_KEY} 58 | EOF 59 | else 60 | touch /tmp/google-credentials.json 61 | fi 62 | 63 | SIDECAR_BIN=mysql-operator-sidecar 64 | VERBOSE="--debug" 65 | 66 | # exec command 67 | case "$1" in 68 | clone-and-init) 69 | shift 1 70 | exec $SIDECAR_BIN $VERBOSE clone-and-init "$@" 71 | ;; 72 | config-and-serve) 73 | shift 1 74 | exec $SIDECAR_BIN $VERBOSE run "$@" 75 | ;; 76 | take-backup-to) 77 | shift 1 78 | exec $SIDECAR_BIN $VERBOSE take-backup-to "$@" 79 | ;; 80 | schedule-backup) 81 | shift 1 82 | exec $SIDECAR_BIN $VERBOSE schedule-backup "$@" 83 | ;; 84 | *) 85 | echo "Usage: $0 {clone-and-init|config-and-serve|take-backup-to|schedule-backup}" 86 | echo "Now runs your command." 87 | echo "$@" 88 | 89 | exec "$@" 90 | esac 91 | -------------------------------------------------------------------------------- /images/mysql-operator-sidecar-8.0/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORMS := linux_amd64 2 | include ../../build/makelib/common.mk 3 | 4 | IMAGE = $(BUILD_REGISTRY)/mysql-operator-sidecar-8.0-$(ARCH) 5 | include ../../build/makelib/image.mk 6 | 7 | img.build: 8 | @$(MAKE) -C ../mysql-operator-sidecar-5.7 IMAGE=$(IMAGE) BUILD_ARGS="--build-arg XTRABACKUP_PKG=percona-xtrabackup-80" 9 | -------------------------------------------------------------------------------- /images/mysql-operator/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copy the mysql-operator binary into a thin image 2 | # The image is pinned to the nonroot tag 3 | FROM gcr.io/distroless/base-debian11@sha256:e76722f06f7c15e0076072fb02782ec59923b0d658b8a3d80bb79deaee6fb44d 4 | 5 | COPY rootfs / 6 | ENTRYPOINT ["/mysql-operator"] 7 | CMD ["help"] 8 | -------------------------------------------------------------------------------- /images/mysql-operator/Makefile: -------------------------------------------------------------------------------- 1 | PLATFORMS := linux_amd64 2 | include ../../build/makelib/common.mk 3 | 4 | IMAGE = $(BUILD_REGISTRY)/mysql-operator-$(ARCH) 5 | CACHE_IMAGES = $(IMAGE) 6 | include ../../build/makelib/image.mk 7 | 8 | img.build: 9 | @$(INFO) docker build $(IMAGE) $(IMAGE_PLATFORM) 10 | @cp -La . $(IMAGE_TEMP_DIR) 11 | @mkdir -p $(IMAGE_TEMP_DIR)/rootfs 12 | @cp $(OUTPUT_DIR)/bin/linux_$(ARCH)/mysql-operator $(IMAGE_TEMP_DIR)/rootfs/mysql-operator 13 | @docker buildx build $(BUILD_ARGS) \ 14 | --platform $(IMAGE_PLATFORM) \ 15 | -t $(IMAGE) \ 16 | --build-arg ARCH=$(ARCH) \ 17 | $(IMAGE_TEMP_DIR) 18 | @$(OK) docker build $(IMAGE) 19 | -------------------------------------------------------------------------------- /pkg/apis/addtoscheme_mysql_v1alpha1.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package apis 18 | 19 | import ( 20 | "github.com/bitpoke/mysql-operator/pkg/apis/mysql/v1alpha1" 21 | ) 22 | 23 | func init() { 24 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back 25 | AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme, v1alpha1.RegisterDefaults) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/apis/apis.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package apis contains Kubernetes API groups. 18 | package apis 19 | 20 | import ( 21 | "k8s.io/apimachinery/pkg/runtime" 22 | ) 23 | 24 | // AddToSchemes may be used to add all resources defined in the project to a Scheme 25 | var AddToSchemes runtime.SchemeBuilder 26 | 27 | // AddToScheme adds all Resources to the Scheme 28 | func AddToScheme(s *runtime.Scheme) error { 29 | return AddToSchemes.AddToScheme(s) 30 | } 31 | -------------------------------------------------------------------------------- /pkg/apis/mysql/group.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package mysql contains mysql API versions 18 | package mysql 19 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/common_types.go: -------------------------------------------------------------------------------- 1 | package v1alpha1 2 | 3 | import "sigs.k8s.io/controller-runtime/pkg/client" 4 | 5 | // MysqlResourceDeletionPolicy delete policy defined 6 | type MysqlResourceDeletionPolicy string 7 | 8 | const ( 9 | // MysqlResourceDeletionPolicyAnnotationKey delete policy key defined 10 | MysqlResourceDeletionPolicyAnnotationKey = "mysql-operator.presslabs.org/resourceDeletionPolicy" 11 | // MysqlResourceDeletionPolicyDelete delete policy delete 12 | MysqlResourceDeletionPolicyDelete = MysqlResourceDeletionPolicy("delete") 13 | // MysqlResourceDeletionPolicyRetain delete policy retain 14 | MysqlResourceDeletionPolicyRetain = MysqlResourceDeletionPolicy("retain") 15 | ) 16 | 17 | // DeletionPolicyRetain parse obj is need to retain 18 | func DeletionPolicyRetain(obj client.Object) bool { 19 | annotations := obj.GetAnnotations() 20 | return annotations != nil && MysqlResourceDeletionPolicy(annotations[MysqlResourceDeletionPolicyAnnotationKey]) == MysqlResourceDeletionPolicyRetain 21 | } 22 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1alpha1 contains API Schema definitions for the mysql v1alpha1 API group 18 | // +kubebuilder:object:generate:=true 19 | // +groupName=mysql.presslabs.org 20 | // 21 | package v1alpha1 22 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/mysqlbackup_defaults.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | // SetDefaults_MysqlBackup sets the defaults for a mysqlbackup object 20 | // nolint: golint 21 | func SetDefaults_MysqlBackup(b *MysqlBackup) { 22 | if len(b.Spec.RemoteDeletePolicy) == 0 { 23 | b.Spec.RemoteDeletePolicy = Retain 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/mysqlbackup_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1alpha1 18 | 19 | import ( 20 | "context" 21 | 22 | . "github.com/onsi/ginkgo" 23 | . "github.com/onsi/gomega" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | ) 27 | 28 | // nolint: errcheck 29 | var _ = Describe("MysqlBackup CRUD", func() { 30 | var created *MysqlBackup 31 | var key types.NamespacedName 32 | 33 | BeforeEach(func() { 34 | key = types.NamespacedName{Name: "foo", Namespace: "default"} 35 | created = &MysqlBackup{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"}} 36 | 37 | }) 38 | 39 | AfterEach(func() { 40 | c.Delete(context.TODO(), created) 41 | }) 42 | 43 | Context("for a valid config", func() { 44 | It("should provide CRUD access to the object", func() { 45 | // Test Create 46 | fetched := &MysqlBackup{} 47 | Expect(c.Create(context.TODO(), created)).NotTo(HaveOccurred()) 48 | 49 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 50 | Expect(fetched).To(Equal(created)) 51 | 52 | // Test Updating the Labels 53 | updated := fetched.DeepCopy() 54 | updated.Labels = map[string]string{"hello": "world"} 55 | Expect(c.Update(context.TODO(), updated)).NotTo(HaveOccurred()) 56 | 57 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 58 | Expect(fetched).To(Equal(updated)) 59 | 60 | // Test Delete 61 | Expect(c.Delete(context.TODO(), fetched)).NotTo(HaveOccurred()) 62 | Expect(c.Get(context.TODO(), key, fetched)).To(HaveOccurred()) 63 | 64 | }) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/mysqlcluster_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package v1alpha1 19 | 20 | import ( 21 | "context" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | "k8s.io/apimachinery/pkg/types" 27 | ) 28 | 29 | var _ = Describe("MysqlCluster CRUD", func() { 30 | var created *MysqlCluster 31 | var key types.NamespacedName 32 | 33 | BeforeEach(func() { 34 | key = types.NamespacedName{Name: "foo", Namespace: "default"} 35 | created = &MysqlCluster{ 36 | ObjectMeta: metav1.ObjectMeta{ 37 | Name: "foo", 38 | Namespace: "default", 39 | }, 40 | Spec: MysqlClusterSpec{ 41 | SecretName: "foo", 42 | }, 43 | } 44 | }) 45 | 46 | AfterEach(func() { 47 | c.Delete(context.TODO(), created) 48 | }) 49 | 50 | Context("for a valid config", func() { 51 | It("should provide CRUD access to the object", func() { 52 | // Test Create 53 | fetched := &MysqlCluster{} 54 | Expect(c.Create(context.TODO(), created)).NotTo(HaveOccurred()) 55 | 56 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 57 | Expect(fetched).To(Equal(created)) 58 | 59 | // Test Updating the Labels 60 | updated := fetched.DeepCopy() 61 | updated.Labels = map[string]string{"hello": "world"} 62 | Expect(c.Update(context.TODO(), updated)).NotTo(HaveOccurred()) 63 | 64 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 65 | Expect(fetched).To(Equal(updated)) 66 | 67 | // Test Delete 68 | Expect(c.Delete(context.TODO(), fetched)).NotTo(HaveOccurred()) 69 | Expect(c.Get(context.TODO(), key, fetched)).To(HaveOccurred()) 70 | 71 | }) 72 | }) 73 | 74 | Context("defaulting functions on MySQLCluster", func() { 75 | var ( 76 | cluster *MysqlCluster 77 | ) 78 | 79 | BeforeEach(func() { 80 | cluster = &MysqlCluster{ObjectMeta: metav1.ObjectMeta{Name: "foo1", Namespace: "default"}} 81 | }) 82 | 83 | It("should populate fields defaults", func() { 84 | SetDefaults_MysqlCluster(cluster) 85 | 86 | Expect(cluster.Spec.MysqlConf).NotTo(BeNil()) 87 | }) 88 | }) 89 | 90 | }) 91 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/mysqldatabase_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package v1alpha1 19 | 20 | import ( 21 | "context" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | "k8s.io/apimachinery/pkg/types" 27 | ) 28 | 29 | var _ = Describe("MysqlDatabase CRUD", func() { 30 | var created *MysqlDatabase 31 | var key types.NamespacedName 32 | 33 | BeforeEach(func() { 34 | key = types.NamespacedName{Name: "foo", Namespace: "default"} 35 | created = &MysqlDatabase{ 36 | ObjectMeta: metav1.ObjectMeta{ 37 | Name: "foo", 38 | Namespace: "default", 39 | }, 40 | Spec: MysqlDatabaseSpec{ 41 | Database: "foo", 42 | }, 43 | } 44 | }) 45 | 46 | AfterEach(func() { 47 | c.Delete(context.TODO(), created) 48 | }) 49 | 50 | Context("for a valid config", func() { 51 | It("should provide CRUD access to the object", func() { 52 | // Test Create 53 | fetched := &MysqlDatabase{} 54 | Expect(c.Create(context.TODO(), created)).NotTo(HaveOccurred()) 55 | 56 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 57 | Expect(fetched).To(Equal(created)) 58 | 59 | // Test Updating the Labels 60 | updated := fetched.DeepCopy() 61 | updated.Labels = map[string]string{"hello": "world"} 62 | Expect(c.Update(context.TODO(), updated)).NotTo(HaveOccurred()) 63 | 64 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 65 | Expect(fetched).To(Equal(updated)) 66 | 67 | // Test Delete 68 | Expect(c.Delete(context.TODO(), fetched)).NotTo(HaveOccurred()) 69 | Expect(c.Get(context.TODO(), key, fetched)).To(HaveOccurred()) 70 | 71 | }) 72 | }) 73 | 74 | }) 75 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/mysqluser_types_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package v1alpha1 19 | 20 | import ( 21 | "context" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 | "k8s.io/apimachinery/pkg/types" 27 | ) 28 | 29 | var _ = Describe("MysqlUser CRUD", func() { 30 | var created *MysqlUser 31 | var key types.NamespacedName 32 | 33 | BeforeEach(func() { 34 | key = types.NamespacedName{Name: "foo", Namespace: "default"} 35 | created = &MysqlUser{ 36 | ObjectMeta: metav1.ObjectMeta{ 37 | Name: "foo", 38 | Namespace: "default", 39 | }, 40 | Spec: MysqlUserSpec{ 41 | User: "foo", 42 | AllowedHosts: []string{"%"}, 43 | }, 44 | } 45 | }) 46 | 47 | AfterEach(func() { 48 | c.Delete(context.TODO(), created) 49 | }) 50 | 51 | Context("for a valid config", func() { 52 | It("should provide CRUD access to the object", func() { 53 | // Test Create 54 | fetched := &MysqlUser{} 55 | Expect(c.Create(context.TODO(), created)).NotTo(HaveOccurred()) 56 | 57 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 58 | Expect(fetched).To(Equal(created)) 59 | 60 | // Test Updating the Labels 61 | updated := fetched.DeepCopy() 62 | updated.Labels = map[string]string{"hello": "world"} 63 | Expect(c.Update(context.TODO(), updated)).NotTo(HaveOccurred()) 64 | 65 | Expect(c.Get(context.TODO(), key, fetched)).NotTo(HaveOccurred()) 66 | Expect(fetched).To(Equal(updated)) 67 | 68 | // Test Delete 69 | Expect(c.Delete(context.TODO(), fetched)).NotTo(HaveOccurred()) 70 | Expect(c.Get(context.TODO(), key, fetched)).To(HaveOccurred()) 71 | 72 | }) 73 | }) 74 | 75 | }) 76 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/register.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // NOTE: Boilerplate only. Ignore this file. 18 | 19 | package v1alpha1 20 | 21 | import ( 22 | "k8s.io/apimachinery/pkg/runtime/schema" 23 | "sigs.k8s.io/controller-runtime/pkg/scheme" 24 | ) 25 | 26 | var ( 27 | // SchemeGroupVersion is group version used to register these objects 28 | SchemeGroupVersion = schema.GroupVersion{Group: "mysql.presslabs.org", Version: "v1alpha1"} 29 | 30 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 31 | SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} 32 | ) 33 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/v1alpha1_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package v1alpha1 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/client" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest" 30 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 31 | ) 32 | 33 | // nolint: errcheck 34 | 35 | var t *envtest.Environment 36 | var cfg *rest.Config 37 | var c client.Client 38 | 39 | func TestV1alpha1(t *testing.T) { 40 | RegisterFailHandler(Fail) 41 | RunSpecsWithDefaultAndCustomReporters(t, "API v1 Suite", []Reporter{printer.NewlineReporter{}}) 42 | } 43 | 44 | var _ = BeforeSuite(func() { 45 | var err error 46 | 47 | t = &envtest.Environment{ 48 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "config", "crd", "bases")}, 49 | } 50 | 51 | err = SchemeBuilder.AddToScheme(scheme.Scheme) 52 | Expect(err).NotTo(HaveOccurred()) 53 | 54 | cfg, err = t.Start() 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 58 | Expect(err).NotTo(HaveOccurred()) 59 | }) 60 | 61 | var _ = AfterSuite(func() { 62 | t.Stop() 63 | }) 64 | -------------------------------------------------------------------------------- /pkg/apis/mysql/v1alpha1/zz_generated.defaults.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | /* 5 | Copyright 2019 Pressinfra SRL 6 | 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | 11 | http://www.apache.org/licenses/LICENSE-2.0 12 | 13 | Unless required by applicable law or agreed to in writing, software 14 | distributed under the License is distributed on an "AS IS" BASIS, 15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | See the License for the specific language governing permissions and 17 | limitations under the License. 18 | */ 19 | 20 | // Code generated by main. DO NOT EDIT. 21 | 22 | package v1alpha1 23 | 24 | import ( 25 | runtime "k8s.io/apimachinery/pkg/runtime" 26 | ) 27 | 28 | // RegisterDefaults adds defaulters functions to the given scheme. 29 | // Public to allow building arbitrary schemes. 30 | // All generated defaulters are covering - they call all nested defaulters. 31 | func RegisterDefaults(scheme *runtime.Scheme) error { 32 | scheme.AddTypeDefaultingFunc(&MysqlBackup{}, func(obj interface{}) { SetObjectDefaults_MysqlBackup(obj.(*MysqlBackup)) }) 33 | scheme.AddTypeDefaultingFunc(&MysqlBackupList{}, func(obj interface{}) { SetObjectDefaults_MysqlBackupList(obj.(*MysqlBackupList)) }) 34 | scheme.AddTypeDefaultingFunc(&MysqlCluster{}, func(obj interface{}) { SetObjectDefaults_MysqlCluster(obj.(*MysqlCluster)) }) 35 | scheme.AddTypeDefaultingFunc(&MysqlClusterList{}, func(obj interface{}) { SetObjectDefaults_MysqlClusterList(obj.(*MysqlClusterList)) }) 36 | return nil 37 | } 38 | 39 | func SetObjectDefaults_MysqlBackup(in *MysqlBackup) { 40 | SetDefaults_MysqlBackup(in) 41 | } 42 | 43 | func SetObjectDefaults_MysqlBackupList(in *MysqlBackupList) { 44 | for i := range in.Items { 45 | a := &in.Items[i] 46 | SetObjectDefaults_MysqlBackup(a) 47 | } 48 | } 49 | 50 | func SetObjectDefaults_MysqlCluster(in *MysqlCluster) { 51 | SetDefaults_MysqlCluster(in) 52 | } 53 | 54 | func SetObjectDefaults_MysqlClusterList(in *MysqlClusterList) { 55 | for i := range in.Items { 56 | a := &in.Items[i] 57 | SetObjectDefaults_MysqlCluster(a) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pkg/controller/add_mysqlbackup.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "github.com/bitpoke/mysql-operator/pkg/controller/mysqlbackup" 21 | ) 22 | 23 | func init() { 24 | // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. 25 | AddToManagerFuncs = append(AddToManagerFuncs, mysqlbackup.Add) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/add_mysqlbackupcron.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "github.com/bitpoke/mysql-operator/pkg/controller/mysqlbackupcron" 21 | ) 22 | 23 | func init() { 24 | // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. 25 | AddToManagerFuncs = append(AddToManagerFuncs, mysqlbackupcron.Add) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/add_mysqlcluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "github.com/bitpoke/mysql-operator/pkg/controller/mysqlcluster" 21 | ) 22 | 23 | func init() { 24 | // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. 25 | AddToManagerFuncs = append(AddToManagerFuncs, mysqlcluster.Add) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/add_mysqldatabase.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "github.com/bitpoke/mysql-operator/pkg/controller/mysqldatabase" 21 | ) 22 | 23 | func init() { 24 | // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. 25 | AddToManagerFuncs = append(AddToManagerFuncs, mysqldatabase.Add) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/add_mysqluser.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import "github.com/bitpoke/mysql-operator/pkg/controller/mysqluser" 20 | 21 | func init() { 22 | // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. 23 | AddToManagerFuncs = append(AddToManagerFuncs, mysqluser.Add) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/add_nodecontroller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import "github.com/bitpoke/mysql-operator/pkg/controller/node" 20 | 21 | func init() { 22 | // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. 23 | AddToManagerFuncs = append(AddToManagerFuncs, node.Add) 24 | } 25 | -------------------------------------------------------------------------------- /pkg/controller/add_orchestrator.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "github.com/bitpoke/mysql-operator/pkg/controller/orchestrator" 21 | ) 22 | 23 | func init() { 24 | // AddToManagerFuncs is a list of functions to create controllers and add them to a manager. 25 | AddToManagerFuncs = append(AddToManagerFuncs, orchestrator.Add) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/controller/controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "sigs.k8s.io/controller-runtime/pkg/manager" 21 | ) 22 | 23 | // AddToManagerFuncs is a list of functions to add all Controllers to the Manager 24 | var AddToManagerFuncs []func(manager.Manager) error 25 | 26 | // AddToManager adds all Controllers to the Manager 27 | func AddToManager(m manager.Manager) error { 28 | for _, f := range AddToManagerFuncs { 29 | if err := f(m); err != nil { 30 | return err 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /pkg/controller/internal/testutil/backup.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: golint, errcheck 18 | package testutil 19 | 20 | import ( 21 | "context" 22 | 23 | . "github.com/onsi/gomega" 24 | . "github.com/onsi/gomega/gstruct" 25 | gomegatypes "github.com/onsi/gomega/types" 26 | 27 | core "k8s.io/api/core/v1" 28 | "sigs.k8s.io/controller-runtime/pkg/client" 29 | 30 | api "github.com/bitpoke/mysql-operator/pkg/apis/mysql/v1alpha1" 31 | ) 32 | 33 | // ListAllBackupsFn returns a helper function that can be used with gomega 34 | // Eventually and Consistently 35 | func ListAllBackupsFn(c client.Client, options client.ListOption) func() []api.MysqlBackup { 36 | return func() []api.MysqlBackup { 37 | backups := &api.MysqlBackupList{} 38 | Expect(c.List(context.TODO(), backups, options)).To(Succeed()) 39 | return backups.Items 40 | } 41 | } 42 | 43 | // BackupHaveCondition is a helper func that returns a matcher to check for an 44 | // existing condition in condition list list 45 | func BackupHaveCondition(condType api.BackupConditionType, status core.ConditionStatus) gomegatypes.GomegaMatcher { 46 | return PointTo(MatchFields(IgnoreExtras, Fields{ 47 | "Status": MatchFields(IgnoreExtras, Fields{ 48 | "Conditions": ContainElement(MatchFields(IgnoreExtras, Fields{ 49 | "Type": Equal(condType), 50 | "Status": Equal(status), 51 | })), 52 | }), 53 | })) 54 | } 55 | 56 | // BackupForCluster is gomega matcher that matches a backup which is for given 57 | // cluster 58 | func BackupForCluster(cluster *api.MysqlCluster) gomegatypes.GomegaMatcher { 59 | return MatchFields(IgnoreExtras, Fields{ 60 | "Spec": MatchFields(IgnoreExtras, Fields{ 61 | "ClusterName": Equal(cluster.Name), 62 | }), 63 | }) 64 | } 65 | 66 | // BackupWithName is a gomega matcher that matchers a backup with the given name 67 | func BackupWithName(name string) gomegatypes.GomegaMatcher { 68 | return MatchFields(IgnoreExtras, Fields{ 69 | "ObjectMeta": MatchFields(IgnoreExtras, Fields{ 70 | "ClusterName": Equal(name), 71 | }), 72 | }) 73 | } 74 | -------------------------------------------------------------------------------- /pkg/controller/mysqlbackup/internal/syncer/syncer_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package syncer 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | 27 | "k8s.io/client-go/kubernetes/scheme" 28 | "k8s.io/client-go/rest" 29 | "sigs.k8s.io/controller-runtime/pkg/client" 30 | "sigs.k8s.io/controller-runtime/pkg/envtest" 31 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 32 | 33 | api "github.com/bitpoke/mysql-operator/pkg/apis/mysql/v1alpha1" 34 | ) 35 | 36 | var t *envtest.Environment 37 | var cfg *rest.Config 38 | var c client.Client 39 | 40 | func TestSyncers(t *testing.T) { 41 | RegisterFailHandler(Fail) 42 | RunSpecsWithDefaultAndCustomReporters(t, "Backup syncers suite", []Reporter{printer.NewlineReporter{}}) 43 | } 44 | 45 | var _ = BeforeSuite(func() { 46 | var err error 47 | 48 | t = &envtest.Environment{ 49 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "..", "config", "crd", "bases")}, 50 | } 51 | 52 | err = api.SchemeBuilder.AddToScheme(scheme.Scheme) 53 | Expect(err).NotTo(HaveOccurred()) 54 | 55 | cfg, err = t.Start() 56 | Expect(err).NotTo(HaveOccurred()) 57 | 58 | c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 59 | Expect(err).NotTo(HaveOccurred()) 60 | }) 61 | 62 | var _ = AfterSuite(func() { 63 | t.Stop() 64 | }) 65 | -------------------------------------------------------------------------------- /pkg/controller/mysqlbackup/mysqlbackup_controller_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package mysqlbackup 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 30 | logf "sigs.k8s.io/controller-runtime/pkg/log" 31 | 32 | "github.com/bitpoke/mysql-operator/pkg/apis" 33 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 34 | ) 35 | 36 | var cfg *rest.Config 37 | var t *envtest.Environment 38 | 39 | func TestMysqlBackupController(t *testing.T) { 40 | RegisterFailHandler(Fail) 41 | RunSpecsWithDefaultAndCustomReporters(t, "MysqlBackup Controller Suite", []Reporter{printer.NewlineReporter{}}) 42 | } 43 | 44 | var _ = BeforeSuite(func() { 45 | var err error 46 | 47 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 48 | 49 | t = &envtest.Environment{ 50 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, 51 | } 52 | 53 | apis.AddToScheme(scheme.Scheme) 54 | 55 | cfg, err = t.Start() 56 | Expect(err).NotTo(HaveOccurred()) 57 | }) 58 | 59 | var _ = AfterSuite(func() { 60 | t.Stop() 61 | }) 62 | -------------------------------------------------------------------------------- /pkg/controller/mysqlbackupcron/mysqlbackupcron_controller_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package mysqlbackupcron 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 30 | logf "sigs.k8s.io/controller-runtime/pkg/log" 31 | 32 | "github.com/bitpoke/mysql-operator/pkg/apis" 33 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 34 | ) 35 | 36 | var cfg *rest.Config 37 | var t *envtest.Environment 38 | 39 | func TestMysqlBackupController(t *testing.T) { 40 | RegisterFailHandler(Fail) 41 | RunSpecsWithDefaultAndCustomReporters(t, "MysqlBackupCron Controller Suite", []Reporter{printer.NewlineReporter{}}) 42 | } 43 | 44 | var _ = BeforeSuite(func() { 45 | var err error 46 | 47 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 48 | 49 | t = &envtest.Environment{ 50 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, 51 | } 52 | 53 | apis.AddToScheme(scheme.Scheme) 54 | 55 | cfg, err = t.Start() 56 | Expect(err).NotTo(HaveOccurred()) 57 | }) 58 | 59 | var _ = AfterSuite(func() { 60 | t.Stop() 61 | }) 62 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/cleaner/pvc_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2018, Platform9 Inc 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package mysqlcluster 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/client" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest" 30 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 31 | logf "sigs.k8s.io/controller-runtime/pkg/log" 32 | 33 | api "github.com/bitpoke/mysql-operator/pkg/apis/mysql/v1alpha1" 34 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 35 | ) 36 | 37 | var t *envtest.Environment 38 | var cfg *rest.Config 39 | var c client.Client 40 | 41 | func TestSyncers(t *testing.T) { 42 | RegisterFailHandler(Fail) 43 | RunSpecsWithDefaultAndCustomReporters(t, "Cleaners suit", []Reporter{printer.NewlineReporter{}}) 44 | } 45 | 46 | var _ = BeforeSuite(func() { 47 | var err error 48 | 49 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 50 | 51 | t = &envtest.Environment{ 52 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "..", "config", "crd", "bases")}, 53 | } 54 | 55 | err = api.SchemeBuilder.AddToScheme(scheme.Scheme) 56 | Expect(err).NotTo(HaveOccurred()) 57 | 58 | cfg, err = t.Start() 59 | Expect(err).NotTo(HaveOccurred()) 60 | 61 | c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 62 | Expect(err).NotTo(HaveOccurred()) 63 | }) 64 | 65 | var _ = AfterSuite(func() { 66 | t.Stop() 67 | }) 68 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/errors.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import "fmt" 20 | 21 | // SyncErrCode is the error code 22 | type SyncErrCode int 23 | 24 | const ( 25 | // PodNotFound represents the error when pod is not found 26 | PodNotFound SyncErrCode = iota 27 | ) 28 | 29 | // SyncError is the error type for pod syncer 30 | type SyncError struct { 31 | syncer string 32 | code SyncErrCode 33 | details string 34 | } 35 | 36 | func (e *SyncError) Error() string { 37 | return fmt.Sprintf("%s(%d: %s)", e.syncer, e.code, e.details) 38 | } 39 | 40 | // NewError returns a syncer error 41 | func NewError(code SyncErrCode, syncer, details string) error { 42 | return &SyncError{ 43 | syncer: syncer, 44 | code: code, 45 | details: details, 46 | } 47 | } 48 | 49 | // NewPodNotFoundError returns a PodNotFound error 50 | func NewPodNotFoundError() error { 51 | return &SyncError{ 52 | syncer: "PodSyncer", 53 | code: PodNotFound, 54 | details: "pod was not found", 55 | } 56 | } 57 | 58 | // IsPodNotFound check if it's a PodNotFound error 59 | func IsPodNotFound(err error) bool { 60 | switch t := err.(type) { 61 | default: 62 | return false 63 | case *SyncError: 64 | return t.code == PodNotFound 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/headless_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | core "k8s.io/api/core/v1" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | "k8s.io/apimachinery/pkg/labels" 23 | "k8s.io/apimachinery/pkg/runtime" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | 26 | "github.com/presslabs/controller-util/syncer" 27 | 28 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 29 | ) 30 | 31 | // NewHeadlessSVCSyncer returns a service syncer 32 | func NewHeadlessSVCSyncer(c client.Client, scheme *runtime.Scheme, cluster *mysqlcluster.MysqlCluster) syncer.Interface { 33 | service := &core.Service{ 34 | ObjectMeta: metav1.ObjectMeta{ 35 | Name: cluster.GetNameForResource(mysqlcluster.HeadlessSVC), 36 | Namespace: cluster.Namespace, 37 | }, 38 | } 39 | 40 | return syncer.NewObjectSyncer("HeadlessSVC", nil, service, c, func() error { 41 | // add general labels to this service 42 | service.Labels = map[string]string{ 43 | "app.kubernetes.io/name": "mysql", 44 | "app.kubernetes.io/managed-by": "mysql.presslabs.org", 45 | } 46 | service.Labels["mysql.presslabs.org/service-type"] = "namespace-nodes" 47 | 48 | service.Spec.ClusterIP = "None" 49 | service.Spec.Selector = labels.Set{ 50 | "app.kubernetes.io/name": "mysql", 51 | "app.kubernetes.io/managed-by": "mysql.presslabs.org", 52 | } 53 | // we want to be able to access pods even if the pod is not ready because the operator should update 54 | // the in memory table to mark the pod ready. 55 | service.Spec.PublishNotReadyAddresses = true 56 | 57 | if len(service.Spec.Ports) != 2 { 58 | service.Spec.Ports = make([]core.ServicePort, 2) 59 | } 60 | service.Spec.Ports[0].Name = MysqlPortName 61 | service.Spec.Ports[0].Port = MysqlPort 62 | service.Spec.Ports[0].TargetPort = TargetPort 63 | service.Spec.Ports[0].Protocol = core.ProtocolTCP 64 | 65 | service.Spec.Ports[1].Name = ExporterPortName 66 | service.Spec.Ports[1].Port = ExporterPort 67 | service.Spec.Ports[1].TargetPort = ExporterTargetPort 68 | service.Spec.Ports[1].Protocol = core.ProtocolTCP 69 | 70 | return nil 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/healthy_replicas_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | "github.com/presslabs/controller-util/syncer" 21 | core "k8s.io/api/core/v1" 22 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | "k8s.io/apimachinery/pkg/runtime" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | 26 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 27 | ) 28 | 29 | // NewHealthyReplicasSVCSyncer returns a service syncer for healthy replicas service 30 | func NewHealthyReplicasSVCSyncer(c client.Client, scheme *runtime.Scheme, cluster *mysqlcluster.MysqlCluster) syncer.Interface { 31 | service := &core.Service{ 32 | ObjectMeta: metav1.ObjectMeta{ 33 | Name: cluster.GetNameForResource(mysqlcluster.HealthyReplicasService), 34 | Namespace: cluster.Namespace, 35 | }, 36 | } 37 | 38 | return syncer.NewObjectSyncer("HealthyReplicasSVC", cluster.Unwrap(), service, c, func() error { 39 | // set service labels 40 | service.Labels = cluster.GetLabels() 41 | service.Labels["mysql.presslabs.org/service-type"] = "ready-replicas" 42 | 43 | // set selectors for healthy replica (non-master) mysql pods only 44 | service.Spec.Selector = cluster.GetSelectorLabels() 45 | service.Spec.Selector["role"] = labelReplica 46 | service.Spec.Selector["healthy"] = labelHealthy 47 | 48 | if len(service.Spec.Ports) != 2 { 49 | service.Spec.Ports = make([]core.ServicePort, 2) 50 | } 51 | 52 | service.Spec.Ports[0].Name = MysqlPortName 53 | service.Spec.Ports[0].Port = MysqlPort 54 | service.Spec.Ports[0].TargetPort = TargetPort 55 | service.Spec.Ports[0].Protocol = core.ProtocolTCP 56 | 57 | service.Spec.Ports[1].Name = SidecarServerPortName 58 | service.Spec.Ports[1].Port = SidecarServerPort 59 | service.Spec.Ports[1].Protocol = core.ProtocolTCP 60 | 61 | return nil 62 | }) 63 | 64 | } 65 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/healthy_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | core "k8s.io/api/core/v1" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | "k8s.io/apimachinery/pkg/runtime" 23 | "sigs.k8s.io/controller-runtime/pkg/client" 24 | 25 | "github.com/presslabs/controller-util/syncer" 26 | 27 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 28 | ) 29 | 30 | // NewHealthySVCSyncer returns a service syncer 31 | func NewHealthySVCSyncer(c client.Client, scheme *runtime.Scheme, cluster *mysqlcluster.MysqlCluster) syncer.Interface { 32 | service := &core.Service{ 33 | ObjectMeta: metav1.ObjectMeta{ 34 | Name: cluster.GetNameForResource(mysqlcluster.HealthyNodesService), 35 | Namespace: cluster.Namespace, 36 | }, 37 | } 38 | 39 | return syncer.NewObjectSyncer("HealthySVC", cluster.Unwrap(), service, c, func() error { 40 | // set service labels 41 | service.Labels = cluster.GetLabels() 42 | service.Labels["mysql.presslabs.org/service-type"] = "ready-nodes" 43 | 44 | service.Spec.Type = "ClusterIP" 45 | service.Spec.Selector = cluster.GetSelectorLabels() 46 | service.Spec.Selector["healthy"] = "yes" 47 | 48 | if len(service.Spec.Ports) != 1 { 49 | service.Spec.Ports = make([]core.ServicePort, 1) 50 | } 51 | service.Spec.Ports[0].Name = MysqlPortName 52 | service.Spec.Ports[0].Port = MysqlPort 53 | service.Spec.Ports[0].TargetPort = TargetPort 54 | service.Spec.Ports[0].Protocol = core.ProtocolTCP 55 | 56 | return nil 57 | }) 58 | } 59 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/master_service.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | "github.com/presslabs/controller-util/syncer" 21 | core "k8s.io/api/core/v1" 22 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 | "k8s.io/apimachinery/pkg/runtime" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | 26 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 27 | ) 28 | 29 | // NewMasterSVCSyncer returns a service syncer for master service 30 | func NewMasterSVCSyncer(c client.Client, scheme *runtime.Scheme, cluster *mysqlcluster.MysqlCluster) syncer.Interface { 31 | service := &core.Service{ 32 | ObjectMeta: metav1.ObjectMeta{ 33 | Name: cluster.GetNameForResource(mysqlcluster.MasterService), 34 | Namespace: cluster.Namespace, 35 | }, 36 | } 37 | 38 | return syncer.NewObjectSyncer("MasterSVC", cluster.Unwrap(), service, c, func() error { 39 | // set service labels 40 | service.Labels = cluster.GetLabels() 41 | service.Labels["mysql.presslabs.org/service-type"] = "master" 42 | 43 | // set selectors for master node 44 | service.Spec.Selector = cluster.GetSelectorLabels() 45 | service.Spec.Selector["role"] = labelMaster 46 | 47 | if len(service.Spec.Ports) != 2 { 48 | service.Spec.Ports = make([]core.ServicePort, 2) 49 | } 50 | service.Spec.Ports[0].Name = MysqlPortName 51 | service.Spec.Ports[0].Port = MysqlPort 52 | service.Spec.Ports[0].TargetPort = TargetPort 53 | service.Spec.Ports[0].Protocol = core.ProtocolTCP 54 | 55 | service.Spec.Ports[1].Name = SidecarServerPortName 56 | service.Spec.Ports[1].Port = SidecarServerPort 57 | service.Spec.Ports[1].Protocol = core.ProtocolTCP 58 | 59 | return nil 60 | }) 61 | 62 | } 63 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/mysqlcluster_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package mysqlcluster 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/client" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest" 30 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 31 | 32 | api "github.com/bitpoke/mysql-operator/pkg/apis/mysql/v1alpha1" 33 | ) 34 | 35 | var t *envtest.Environment 36 | var cfg *rest.Config 37 | var c client.Client 38 | 39 | func TestSyncers(t *testing.T) { 40 | RegisterFailHandler(Fail) 41 | RunSpecsWithDefaultAndCustomReporters(t, "Syncers suit", []Reporter{printer.NewlineReporter{}}) 42 | } 43 | 44 | var _ = BeforeSuite(func() { 45 | var err error 46 | 47 | t = &envtest.Environment{ 48 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "..", "..", "config", "crd", "bases")}, 49 | } 50 | 51 | err = api.SchemeBuilder.AddToScheme(scheme.Scheme) 52 | Expect(err).NotTo(HaveOccurred()) 53 | 54 | cfg, err = t.Start() 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | c, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 58 | Expect(err).NotTo(HaveOccurred()) 59 | }) 60 | 61 | var _ = AfterSuite(func() { 62 | t.Stop() 63 | }) 64 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/pdb.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | policy "k8s.io/api/policy/v1" 21 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 22 | "k8s.io/apimachinery/pkg/runtime" 23 | "k8s.io/apimachinery/pkg/util/intstr" 24 | "sigs.k8s.io/controller-runtime/pkg/client" 25 | 26 | "github.com/presslabs/controller-util/syncer" 27 | 28 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 29 | ) 30 | 31 | // NewPDBSyncer returns the syncer for pdb 32 | func NewPDBSyncer(c client.Client, scheme *runtime.Scheme, cluster *mysqlcluster.MysqlCluster) syncer.Interface { 33 | pdb := &policy.PodDisruptionBudget{ 34 | ObjectMeta: metav1.ObjectMeta{ 35 | Name: cluster.GetNameForResource(mysqlcluster.PodDisruptionBudget), 36 | Namespace: cluster.Namespace, 37 | }, 38 | } 39 | 40 | return syncer.NewObjectSyncer("PDB", cluster.Unwrap(), pdb, c, func() error { 41 | if pdb.Spec.MinAvailable != nil { 42 | // this mean that pdb is created and should return because spec is imutable 43 | return nil 44 | } 45 | ma := intstr.FromString(cluster.Spec.MinAvailable) 46 | pdb.Spec.MinAvailable = &ma 47 | pdb.Spec.Selector = metav1.SetAsLabelSelector(cluster.GetSelectorLabels()) 48 | return nil 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/internal/syncer/secret.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/presslabs/controller-util/syncer" 23 | core "k8s.io/api/core/v1" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/runtime" 26 | "sigs.k8s.io/controller-runtime/pkg/client" 27 | 28 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 29 | "github.com/bitpoke/mysql-operator/pkg/options" 30 | ) 31 | 32 | // NewSecretSyncer returns secret syncer 33 | // nolint: gocyclo 34 | // TODO: this syncer is not needed anymore and can be removed in future version (v0.4) 35 | func NewSecretSyncer(c client.Client, scheme *runtime.Scheme, cluster *mysqlcluster.MysqlCluster, opt *options.Options) syncer.Interface { 36 | secret := &core.Secret{ 37 | ObjectMeta: metav1.ObjectMeta{ 38 | Name: cluster.Spec.SecretName, 39 | Namespace: cluster.Namespace, 40 | }, 41 | } 42 | 43 | return syncer.NewObjectSyncer("Secret", nil, secret, c, func() error { 44 | if _, ok := secret.Data["ROOT_PASSWORD"]; !ok { 45 | return fmt.Errorf("ROOT_PASSWORD not set in secret: %s", secret.Name) 46 | } 47 | 48 | return nil 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/controller/mysqlcluster/mysqlcluster_controller_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package mysqlcluster 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 30 | logf "sigs.k8s.io/controller-runtime/pkg/log" 31 | 32 | "github.com/bitpoke/mysql-operator/pkg/apis" 33 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 34 | ) 35 | 36 | var cfg *rest.Config 37 | var t *envtest.Environment 38 | 39 | func TestMysqlClusterController(t *testing.T) { 40 | RegisterFailHandler(Fail) 41 | RunSpecsWithDefaultAndCustomReporters(t, "MysqlCluster Controller Suite", []Reporter{printer.NewlineReporter{}}) 42 | } 43 | 44 | var _ = BeforeSuite(func() { 45 | var err error 46 | 47 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 48 | 49 | t = &envtest.Environment{ 50 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, 51 | } 52 | 53 | apis.AddToScheme(scheme.Scheme) 54 | 55 | cfg, err = t.Start() 56 | Expect(err).NotTo(HaveOccurred()) 57 | }) 58 | 59 | var _ = AfterSuite(func() { 60 | t.Stop() 61 | }) 62 | -------------------------------------------------------------------------------- /pkg/controller/mysqldatabase/db_controller_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqldatabase 18 | 19 | import ( 20 | "path/filepath" 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | "k8s.io/client-go/kubernetes/scheme" 26 | "k8s.io/client-go/rest" 27 | "sigs.k8s.io/controller-runtime/pkg/envtest" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 29 | logf "sigs.k8s.io/controller-runtime/pkg/log" 30 | 31 | "github.com/bitpoke/mysql-operator/pkg/apis" 32 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 33 | ) 34 | 35 | var cfg *rest.Config 36 | var t *envtest.Environment 37 | 38 | func TestMySQLDatabase(t *testing.T) { 39 | RegisterFailHandler(Fail) 40 | RunSpecsWithDefaultAndCustomReporters(t, "MySQL Database Suite", []Reporter{printer.NewlineReporter{}}) 41 | } 42 | 43 | var _ = BeforeSuite(func() { 44 | var err error 45 | 46 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 47 | 48 | t = &envtest.Environment{ 49 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, 50 | } 51 | 52 | apis.AddToScheme(scheme.Scheme) 53 | 54 | cfg, err = t.Start() 55 | Expect(err).NotTo(HaveOccurred()) 56 | }) 57 | 58 | var _ = AfterSuite(func() { 59 | t.Stop() 60 | }) 61 | -------------------------------------------------------------------------------- /pkg/controller/mysqluser/mysqluser_controller_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqluser 18 | 19 | import ( 20 | "path/filepath" 21 | "testing" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | "k8s.io/client-go/kubernetes/scheme" 26 | "k8s.io/client-go/rest" 27 | "sigs.k8s.io/controller-runtime/pkg/envtest" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 29 | logf "sigs.k8s.io/controller-runtime/pkg/log" 30 | 31 | "github.com/bitpoke/mysql-operator/pkg/apis" 32 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 33 | ) 34 | 35 | var cfg *rest.Config 36 | var t *envtest.Environment 37 | 38 | func TestMySQLUserController(t *testing.T) { 39 | RegisterFailHandler(Fail) 40 | RunSpecsWithDefaultAndCustomReporters(t, "MysqlUser Controller Suite", []Reporter{printer.NewlineReporter{}}) 41 | } 42 | 43 | var _ = BeforeSuite(func() { 44 | var err error 45 | 46 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 47 | 48 | t = &envtest.Environment{ 49 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, 50 | } 51 | 52 | apis.AddToScheme(scheme.Scheme) 53 | 54 | cfg, err = t.Start() 55 | Expect(err).NotTo(HaveOccurred()) 56 | }) 57 | 58 | var _ = AfterSuite(func() { 59 | t.Stop() 60 | }) 61 | -------------------------------------------------------------------------------- /pkg/controller/node/node_ctrl_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package node 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | 24 | . "github.com/onsi/ginkgo" 25 | . "github.com/onsi/gomega" 26 | "k8s.io/client-go/kubernetes/scheme" 27 | "k8s.io/client-go/rest" 28 | "sigs.k8s.io/controller-runtime/pkg/envtest" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 30 | logf "sigs.k8s.io/controller-runtime/pkg/log" 31 | 32 | "github.com/bitpoke/mysql-operator/pkg/apis" 33 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 34 | ) 35 | 36 | var cfg *rest.Config 37 | var t *envtest.Environment 38 | 39 | func TestMysqlNodeController(t *testing.T) { 40 | RegisterFailHandler(Fail) 41 | RunSpecsWithDefaultAndCustomReporters(t, "MysqlNode Controller Suite", []Reporter{printer.NewlineReporter{}}) 42 | } 43 | 44 | var _ = BeforeSuite(func() { 45 | var err error 46 | 47 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 48 | 49 | t = &envtest.Environment{ 50 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, 51 | } 52 | 53 | apis.AddToScheme(scheme.Scheme) 54 | 55 | cfg, err = t.Start() 56 | Expect(err).NotTo(HaveOccurred()) 57 | }) 58 | 59 | var _ = AfterSuite(func() { 60 | t.Stop() 61 | }) 62 | -------------------------------------------------------------------------------- /pkg/controller/node/sql_fake_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package node 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | . "github.com/onsi/ginkgo" 24 | . "github.com/onsi/gomega" 25 | ) 26 | 27 | type fakeSQLRunner struct{} 28 | 29 | // test if fakeer implements interface 30 | var _ SQLInterface = &fakeSQLRunner{} 31 | 32 | func (f *fakeSQLRunner) Wait(ctx context.Context) error { 33 | return nil 34 | } 35 | 36 | func (f *fakeSQLRunner) DisableSuperReadOnly(ctx context.Context) (func(), error) { 37 | return func() {}, nil 38 | } 39 | 40 | func (f *fakeSQLRunner) ChangeMasterTo(ctx context.Context, host, user, pass string) error { 41 | return nil 42 | } 43 | 44 | func (f *fakeSQLRunner) MarkConfigurationDone(ctx context.Context) error { 45 | return nil 46 | } 47 | 48 | func (f *fakeSQLRunner) Host() string { 49 | return "" 50 | } 51 | 52 | func (f *fakeSQLRunner) SetPurgedGTID(ctx context.Context) error { 53 | return nil 54 | } 55 | 56 | func (f *fakeSQLRunner) IsConfigured(ctx context.Context) (bool, error) { 57 | return false, nil 58 | } 59 | 60 | func (f *fakeSQLRunner) MarkSetGTIDPurged(ctx context.Context) error { 61 | return nil 62 | } 63 | 64 | var _ = Describe("SQL functions", func() { 65 | It("should find not found error", func() { 66 | err := fmt.Errorf("Error 1146: Table 'a.a' doesn't exist") 67 | Expect(isMySQLError(err, 1146)).To(Equal(true)) 68 | Expect(isMySQLError(err, 1145)).To(Equal(false)) 69 | }) 70 | }) 71 | -------------------------------------------------------------------------------- /pkg/controller/orchestrator/orchestrator_controller_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // nolint: errcheck 18 | package orchestrator 19 | 20 | import ( 21 | "path/filepath" 22 | "testing" 23 | "time" 24 | 25 | . "github.com/onsi/ginkgo" 26 | . "github.com/onsi/gomega" 27 | "k8s.io/client-go/kubernetes/scheme" 28 | "k8s.io/client-go/rest" 29 | "sigs.k8s.io/controller-runtime/pkg/envtest" 30 | "sigs.k8s.io/controller-runtime/pkg/envtest/printer" 31 | logf "sigs.k8s.io/controller-runtime/pkg/log" 32 | 33 | "github.com/bitpoke/mysql-operator/pkg/apis" 34 | "github.com/bitpoke/mysql-operator/pkg/controller/internal/testutil" 35 | ) 36 | 37 | var cfg *rest.Config 38 | var t *envtest.Environment 39 | 40 | func TestMysqlClusterController(t *testing.T) { 41 | RegisterFailHandler(Fail) 42 | RunSpecsWithDefaultAndCustomReporters(t, "Orchestrator Controller Suite", []Reporter{printer.NewlineReporter{}}) 43 | } 44 | 45 | var _ = BeforeSuite(func() { 46 | 47 | // set reconcile time to 1 second to speed up the tests. 48 | reconcileTimePeriod = time.Second 49 | 50 | logf.SetLogger(testutil.NewTestLogger(GinkgoWriter)) 51 | 52 | var err error 53 | 54 | t = &envtest.Environment{ 55 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, 56 | } 57 | 58 | apis.AddToScheme(scheme.Scheme) 59 | 60 | cfg, err = t.Start() 61 | Expect(err).NotTo(HaveOccurred()) 62 | 63 | }) 64 | 65 | var _ = AfterSuite(func() { 66 | t.Stop() 67 | }) 68 | -------------------------------------------------------------------------------- /pkg/controller/orchestrator/orchestrator_syncers.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package orchestrator 18 | 19 | import ( 20 | "github.com/presslabs/controller-util/syncer" 21 | "sigs.k8s.io/controller-runtime/pkg/client" 22 | 23 | api "github.com/bitpoke/mysql-operator/pkg/apis/mysql/v1alpha1" 24 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 25 | orc "github.com/bitpoke/mysql-operator/pkg/orchestrator" 26 | ) 27 | 28 | // newFinalizerSyncer returns a syncer for mysql cluster that sets the OrchestratorFinalizer 29 | func newFinalizerSyncer(c client.Client, cluster *mysqlcluster.MysqlCluster, orcClient orc.Interface) syncer.Interface { 30 | 31 | // create the orchestrator finalizer 32 | return syncer.NewObjectSyncer("OrchestratorFinalizerSyncer", nil, cluster.Unwrap(), c, func() error { 33 | out := cluster.Unwrap() 34 | 35 | // always add finalizer, this action is idempotent 36 | addFinalizer(out, OrchestratorFinalizer) 37 | // TODO: remove this in next version (v0.4) 38 | removeFinalizer(out, OldOrchestratorFinalizer) 39 | 40 | // if cluster is deleted then check orchestrator status and remove finalizer if no node is in orchestrator 41 | if out.DeletionTimestamp != nil { 42 | var ( 43 | instances InstancesSet 44 | err error 45 | ) 46 | // get status from orchestrator 47 | if instances, err = orcClient.Cluster(cluster.GetClusterAlias()); err != nil { 48 | log.V(0).Info("an error occurred while getting cluster from orchestrator", "error", err) 49 | } 50 | 51 | if len(instances) == 0 { 52 | removeFinalizer(out, OrchestratorFinalizer) 53 | } 54 | } 55 | 56 | return nil 57 | }) 58 | } 59 | 60 | func addFinalizer(in *api.MysqlCluster, finalizer string) { 61 | for _, f := range in.Finalizers { 62 | if f == finalizer { 63 | // finalizer already exists 64 | return 65 | } 66 | } 67 | 68 | // initialize list 69 | if len(in.Finalizers) == 0 { 70 | in.Finalizers = []string{} 71 | } 72 | 73 | in.Finalizers = append(in.Finalizers, finalizer) 74 | } 75 | 76 | func removeFinalizer(in *api.MysqlCluster, finalizer string) { 77 | var ( 78 | index int 79 | f string 80 | ) 81 | for index, f = range in.Finalizers { 82 | if f == finalizer { 83 | in.Finalizers = append(in.Finalizers[:index], in.Finalizers[index+1:]...) 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /pkg/internal/mysql/database.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysql 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | ) 23 | 24 | // CreateDatabaseIfNotExists creates a database if it doesn't already exist 25 | func CreateDatabaseIfNotExists(ctx context.Context, sql SQLRunner, database, charset, collate string) error { 26 | args := []interface{}{} 27 | query := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", Escape(database)) 28 | 29 | if len(charset) > 0 { 30 | query += " CHARACTER SET ?" 31 | args = append(args, charset) 32 | } 33 | 34 | if len(collate) > 0 { 35 | query += " COLLATE ?" 36 | args = append(args, collate) 37 | } 38 | 39 | if err := sql.QueryExec(ctx, NewQuery(query, args...)); err != nil { 40 | return fmt.Errorf("failed to create database, err: %s", err) 41 | } 42 | 43 | return nil 44 | } 45 | 46 | // DropDatabase deletes the database 47 | func DropDatabase(ctx context.Context, sql SQLRunner, database string) error { 48 | query := NewQuery(fmt.Sprintf("DROP DATABASE IF EXISTS %s", escapeID(database))) 49 | 50 | if err := sql.QueryExec(ctx, query); err != nil { 51 | return fmt.Errorf("failed to remove database, err: %s", err) 52 | } 53 | 54 | return nil 55 | } 56 | -------------------------------------------------------------------------------- /pkg/internal/mysql/escape.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysql 18 | 19 | // Escape escapes a string 20 | func Escape(sql string) string { 21 | dest := make([]byte, 0, 2*len(sql)) 22 | var escape byte 23 | for i := 0; i < len(sql); i++ { 24 | escape = 0 25 | switch sql[i] { 26 | case 0: /* Must be escaped for 'mysql' */ 27 | escape = '0' 28 | case '\n': /* Must be escaped for logs */ 29 | escape = 'n' 30 | case '\r': 31 | escape = 'r' 32 | case '\\': 33 | escape = '\\' 34 | case '\'': 35 | escape = '\'' 36 | case '"': /* Better safe than sorry */ 37 | escape = '"' 38 | case '\032': /* This gives problems on Win32 */ 39 | escape = 'Z' 40 | } 41 | 42 | if escape != 0 { 43 | dest = append(dest, '\\', escape) 44 | } else { 45 | dest = append(dest, sql[i]) 46 | } 47 | } 48 | 49 | return string(dest) 50 | } 51 | -------------------------------------------------------------------------------- /pkg/internal/mysql/query.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysql 18 | 19 | import ( 20 | "strings" 21 | ) 22 | 23 | // Query contains a escaped query string with variables marked with a question mark (?) and a slice 24 | // of positional arguments 25 | type Query struct { 26 | escapedQuery string 27 | args []interface{} 28 | } 29 | 30 | // String representation of the query 31 | func (q *Query) String() string { 32 | return q.escapedQuery 33 | } 34 | 35 | // Args is used in test 36 | func (q *Query) Args() []interface{} { 37 | return q.args 38 | } 39 | 40 | // NewQuery returns a new Query object 41 | func NewQuery(q string, args ...interface{}) Query { 42 | if q == "" { 43 | panic("unexpected empty query") 44 | } 45 | 46 | if !strings.HasSuffix(q, ";") { 47 | q += ";" 48 | } 49 | 50 | return Query{ 51 | escapedQuery: q, 52 | args: args, 53 | } 54 | } 55 | 56 | // ConcatenateQueries concatenates the provided queries into a single query 57 | func ConcatenateQueries(queries ...Query) Query { 58 | args := []interface{}{} 59 | query := "" 60 | 61 | for _, pq := range queries { 62 | if query != "" { 63 | if !strings.HasSuffix(query, "\n") { 64 | query += "\n" 65 | } 66 | } 67 | 68 | query += pq.escapedQuery 69 | args = append(args, pq.args...) 70 | } 71 | 72 | return NewQuery(query, args...) 73 | } 74 | 75 | // BuildAtomicQuery concatenates the provided queries into a single query wrapped in a BEGIN COMMIT block 76 | func BuildAtomicQuery(queries ...Query) Query { 77 | queries = append([]Query{NewQuery("BEGIN")}, queries...) 78 | queries = append(queries, NewQuery("COMMIT")) 79 | 80 | return ConcatenateQueries(queries...) 81 | } 82 | -------------------------------------------------------------------------------- /pkg/internal/mysqlbackup/defaults.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlbackup 18 | 19 | import ( 20 | "github.com/bitpoke/mysql-operator/pkg/internal/mysqlcluster" 21 | ) 22 | 23 | // SetDefaults sets default for backup 24 | func (w *MysqlBackup) SetDefaults(cluster *mysqlcluster.MysqlCluster) { 25 | w.Spec.BackupURL = w.GetBackupURL(cluster) 26 | 27 | if len(w.Spec.BackupSecretName) == 0 { 28 | w.Spec.BackupSecretName = cluster.Spec.BackupSecretName 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /pkg/internal/mysqlcluster/defaults_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/ginkgo/extensions/table" 22 | . "github.com/onsi/gomega" 23 | 24 | "k8s.io/apimachinery/pkg/api/resource" 25 | ) 26 | 27 | var _ = Describe("MySQL defaults unit tests", func() { 28 | DescribeTable("humanize should not round the value", func(quantity, expect string) { 29 | q := resource.MustParse(quantity) 30 | hq := humanizeSize(q.Value()) 31 | Expect(hq.String()).To(Equal(expect)) 32 | }, 33 | Entry("fractional Gigabytes", "1.5Gi", "1536M"), 34 | Entry("integer Gigabytes", "2Gi", "2G"), 35 | Entry("overflow Megabytes to Gigabytes", "2048Mi", "2G"), 36 | Entry("overflow Kilobytes to Gigabytes", "2097152Ki", "2G"), 37 | Entry("fractional Megabytes", "1.5Mi", "1536K"), 38 | Entry("integer Megabytes", "128Mi", "128M"), 39 | Entry("overflow Kilobytes to Megabytes", "4096Ki", "4M"), 40 | Entry("fractional Kilobytes", "0.5Ki", "512"), 41 | Entry("integer Kilobytes", "1200Ki", "1200K"), 42 | ) 43 | }) 44 | -------------------------------------------------------------------------------- /pkg/internal/mysqlcluster/validation.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package mysqlcluster 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | // Validate checks if the cluster spec is validated 24 | func (c *MysqlCluster) Validate() error { 25 | // TODO: this validation should be done in an admission web-hook 26 | if len(c.Spec.SecretName) == 0 { 27 | return fmt.Errorf("spec.secretName is missing") 28 | } 29 | 30 | if len(c.GetMysqlImage()) == 0 { 31 | return fmt.Errorf("%s is not a valid MySQL version", c.Spec.MysqlVersion) 32 | } 33 | 34 | // volume spec should be specified on the cluster 35 | vs := c.Spec.VolumeSpec 36 | if anyIsNull(vs.PersistentVolumeClaim, vs.HostPath, vs.EmptyDir) { 37 | return fmt.Errorf("no .spec.volumeSpec is specified") 38 | } 39 | 40 | return nil 41 | } 42 | 43 | // anyNull checks if any of the given parameters is null and returns true if so 44 | func anyIsNull(vars ...interface{}) bool { 45 | isNull := false 46 | for _, v := range vars { 47 | isNull = isNull || v == nil 48 | } 49 | return isNull 50 | } 51 | -------------------------------------------------------------------------------- /pkg/orchestrator/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package orchestrator 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | "io" 23 | "io/ioutil" 24 | "net/http" 25 | "net/url" 26 | 27 | logf "sigs.k8s.io/controller-runtime/pkg/log" 28 | ) 29 | 30 | var log = logf.Log.WithName("orchestrator.client") 31 | 32 | func (o *orchestrator) makeGetRequest(path string, out interface{}) *Error { 33 | uri := fmt.Sprintf("%s/%s", o.connectURI, path) 34 | log.V(2).Info("orchestrator request info", "uri", uri) 35 | 36 | req, err := http.NewRequest("GET", uri, nil) 37 | if err != nil { 38 | return NewErrorMsg(fmt.Sprintf("can't create request: %s", err.Error()), path) 39 | } 40 | 41 | client := &http.Client{ 42 | Timeout: o.timeout, 43 | } 44 | resp, err := client.Do(req) 45 | if err != nil { 46 | return NewErrorMsg(err.Error(), path) 47 | } 48 | 49 | if resp.StatusCode >= 500 { 50 | return NewError(resp, path, nil) 51 | } 52 | 53 | if err := unmarshalJSON(resp.Body, out); err != nil { 54 | return NewError(resp, path, err) 55 | } 56 | 57 | return nil 58 | } 59 | 60 | func (o *orchestrator) makeGetAPIRequest(path string, query map[string][]string) error { 61 | args := url.Values(query).Encode() 62 | if len(args) != 0 { 63 | args = "?" + args 64 | } 65 | 66 | path = fmt.Sprintf("%s%s", path, args) 67 | var apiObj struct { 68 | Code string 69 | Message string 70 | } 71 | if err := o.makeGetRequest(path, &apiObj); err != nil { 72 | return err 73 | } 74 | 75 | if apiObj.Code != "OK" { 76 | return fmt.Errorf("orc failed with: %s", apiObj.Message) 77 | } 78 | 79 | return nil 80 | } 81 | 82 | func unmarshalJSON(in io.Reader, obj interface{}) error { 83 | body, err := ioutil.ReadAll(in) 84 | if err != nil { 85 | return fmt.Errorf("io read error: %s", err) 86 | } 87 | 88 | if err = json.Unmarshal(body, obj); err != nil { 89 | log.V(1).Info("error unmarshal data", "body", string(body)) 90 | return err 91 | } 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /pkg/sidecar/apphelper.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sidecar 18 | 19 | // const ( 20 | // // timeOut represents the number of tries to check mysql to be ready. 21 | // timeOut = 60 22 | // // connRetry represents the number of tries to connect to master server 23 | // connRetry = 10 24 | // ) 25 | 26 | // RunSidecarCommand is the main command, and represents the runtime helper that 27 | // configures the mysql server 28 | func RunSidecarCommand(cfg *Config, stop <-chan struct{}) error { 29 | log.Info("start http server for backups") 30 | srv := newServer(cfg, stop) 31 | return srv.ListenAndServe() 32 | } 33 | -------------------------------------------------------------------------------- /pkg/sidecar/apphelper_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sidecar 18 | 19 | // import ( 20 | // "strings" 21 | // 22 | // . "github.com/onsi/ginkgo" 23 | // . "github.com/onsi/gomega" 24 | // ) 25 | 26 | // var _ = Describe("Test sidecar apphelper", func() { 27 | // It("should find the single gtid set in backup", func() { 28 | // var ( 29 | // source string = "mysql-bin.000002 6552870 684ca0cf-495e-11e9-9fe8-0a580af407e9:1-176661\n" 30 | // result string = "684ca0cf-495e-11e9-9fe8-0a580af407e9:1-176661" 31 | // ) 32 | // Expect(getGTIDFrom(strings.NewReader(source))).To(Equal(result)) 33 | // }) 34 | // It("should find all gtid sets in backup", func() { 35 | // var ( 36 | // source string = `mysql-bin.006394 154349713 00003306-1111-0000-0000-000000000001:1-48861335, 37 | // 00003306-1111-1111-1111-111111111111:1-11000155952, 38 | // 00003306-2222-2222-2222-222222222222:1-8706021957 39 | // ` 40 | // result string = "00003306-1111-0000-0000-000000000001:1-48861335," + 41 | // "00003306-1111-1111-1111-111111111111:1-11000155952," + 42 | // "00003306-2222-2222-2222-222222222222:1-8706021957" 43 | // ) 44 | // Expect(getGTIDFrom(strings.NewReader(source))).To(Equal(result)) 45 | // }) 46 | // 47 | // }) 48 | -------------------------------------------------------------------------------- /pkg/sidecar/configs_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sidecar 18 | 19 | import ( 20 | . "github.com/onsi/ginkgo" 21 | . "github.com/onsi/gomega" 22 | ) 23 | 24 | var _ = Describe("Test sidecar configs", func() { 25 | var ( 26 | cfg *Config 27 | ) 28 | 29 | BeforeEach(func() { 30 | cfg = &Config{ 31 | Hostname: "cluster-mysql-0", 32 | ClusterName: "cluster", 33 | Namespace: "default", 34 | ServiceName: "mysql", 35 | BackupUser: "backup-user", 36 | BackupPassword: "backup-password", 37 | MyServerIDOffset: MysqlServerIDOffset, 38 | } 39 | }) 40 | 41 | It("should fill the server id", func() { 42 | Expect(cfg.ServerID()).To(Equal(MysqlServerIDOffset)) 43 | 44 | cfg.Hostname = "cluster-mysql-3" 45 | Expect(cfg.ServerID()).To(Equal(MysqlServerIDOffset + 3)) 46 | 47 | cfg.MyServerIDOffset = 200 48 | Expect(cfg.ServerID()).To(Equal(203)) 49 | }) 50 | 51 | It("should get the default master", func() { 52 | Expect(cfg.MasterFQDN()).To(Equal("cluster-mysql-master")) 53 | }) 54 | 55 | It("should determine the host ip", func() { 56 | Expect(retryLookupHost("localhost")).To(ContainElement("127.0.0.1")) 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /pkg/sidecar/constants.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sidecar 18 | 19 | import ( 20 | "strconv" 21 | "time" 22 | 23 | // add mysql driver 24 | _ "github.com/go-sql-driver/mysql" 25 | 26 | "github.com/bitpoke/mysql-operator/pkg/util/constants" 27 | ) 28 | 29 | var ( 30 | // MysqlServerIDOffset represents the offset with which all server ids are shifted from 0 31 | MysqlServerIDOffset = 100 32 | 33 | // MysqlPort represents port on which mysql works 34 | mysqlPort = strconv.Itoa(constants.MysqlPort) 35 | 36 | // ConfigDir is the mysql configs path, /etc/mysql 37 | configDir = constants.ConfVolumeMountPath 38 | 39 | // ConfDPath is /etc/mysql/conf.d 40 | confDPath = constants.ConfDPath 41 | 42 | // MountConfigDir is the mounted configs that needs processing 43 | mountConfigDir = constants.ConfMapVolumeMountPath 44 | 45 | // confClientPath the path where to put the client.conf file 46 | confClientPath = constants.ConfClientPath 47 | 48 | // confHeartbeatPath the path where to put the heartbeat.conf file 49 | confHeartbeatPath = constants.ConfHeartBeatPath 50 | 51 | // DataDir is the mysql data. /var/lib/mysql 52 | dataDir = constants.DataVolumeMountPath 53 | 54 | // ToolsDbName is the name of the tools table 55 | toolsDbName = constants.OperatorDbName 56 | 57 | // toolsHeartbeatTableName is the name used for pt-heartbeat table 58 | toolsHeartbeatTableName = "heartbeat" 59 | 60 | // heartBeatUserName is the MySQL user that is used for pt-heartbeat 61 | heartBeatUserName = "sys_heartbeat" 62 | 63 | // ServerPort http server port 64 | serverPort = constants.SidecarServerPort 65 | // ServerProbeEndpoint is the http server endpoint for probe 66 | serverProbeEndpoint = constants.SidecarServerProbePath 67 | // ServerBackupEndpoint is the http server endpoint for backups 68 | serverBackupEndpoint = "/xbackup" 69 | // ServerDialTimeout is the connect timeout (not http timeout) for requesting a backup from the sidecar server 70 | serverConnectTimeout = 5 * time.Second 71 | 72 | // xtrabackup Executable Name 73 | xtrabackupCommand = "xtrabackup" 74 | 75 | // xbstream Executable Name 76 | xbstreamCommand = "xbstream" 77 | 78 | shPreStop = constants.ShPreStop 79 | ) 80 | -------------------------------------------------------------------------------- /pkg/sidecar/sidecar_suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sidecar 18 | 19 | import ( 20 | "testing" 21 | 22 | . "github.com/onsi/ginkgo" 23 | . "github.com/onsi/gomega" 24 | "k8s.io/klog" 25 | "k8s.io/klog/v2/klogr" 26 | 27 | logf "github.com/presslabs/controller-util/log" 28 | ) 29 | 30 | func TestSidecarApp(t *testing.T) { 31 | klog.SetOutput(GinkgoWriter) 32 | logf.SetLogger(klogr.New()) 33 | 34 | RegisterFailHandler(Fail) 35 | RunSpecs(t, "Sidecar App Suite") 36 | } 37 | -------------------------------------------------------------------------------- /pkg/sidecar/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package sidecar 18 | 19 | import ( 20 | "bufio" 21 | "fmt" 22 | "io" 23 | "os" 24 | 25 | // add mysql driver 26 | _ "github.com/go-sql-driver/mysql" 27 | logf "sigs.k8s.io/controller-runtime/pkg/log" 28 | ) 29 | 30 | var log = logf.Log.WithName("sidecar") 31 | 32 | // copyFile the src file to dst. Any existing file will be overwritten and will not 33 | // copy file attributes. 34 | // nolint: gosec 35 | func copyFile(src, dst string) error { 36 | in, err := os.Open(src) 37 | if err != nil { 38 | return err 39 | } 40 | defer func() { 41 | if err1 := in.Close(); err1 != nil { 42 | log.Error(err1, "failed to close source file", "src_file", src) 43 | } 44 | }() 45 | 46 | out, err := os.Create(dst) 47 | if err != nil { 48 | return err 49 | } 50 | defer func() { 51 | if err1 := out.Close(); err1 != nil { 52 | log.Error(err1, "failed to close destination file", "dest_file", dst) 53 | } 54 | }() 55 | 56 | _, err = io.Copy(out, in) 57 | if err != nil { 58 | return err 59 | } 60 | return nil 61 | } 62 | 63 | // readPurgedGTID returns the GTID from xtrabackup_binlog_info file 64 | func readPurgedGTID() (string, error) { 65 | file, err := os.Open(fmt.Sprintf("%s/xtrabackup_binlog_info", dataDir)) 66 | if err != nil && !os.IsNotExist(err) { 67 | return "", err 68 | } 69 | 70 | if os.IsNotExist(err) { 71 | return "", nil 72 | } 73 | 74 | defer func() { 75 | if err1 := file.Close(); err1 != nil { 76 | log.Error(err1, "failed to close file") 77 | } 78 | }() 79 | 80 | return getGTIDFrom(file) 81 | } 82 | 83 | // getGTIDFrom parse the content from xtrabackup_binlog_info file passed as 84 | // io.Reader and extracts the GTID. 85 | func getGTIDFrom(reader io.Reader) (string, error) { 86 | scanner := bufio.NewScanner(reader) 87 | scanner.Split(bufio.ScanWords) 88 | 89 | gtid := "" 90 | for i := 0; scanner.Scan(); i++ { 91 | if i >= 2 { 92 | gtid += scanner.Text() 93 | } 94 | } 95 | 96 | if err := scanner.Err(); err != nil { 97 | return "", err 98 | } else if len(gtid) == 0 { 99 | return "", io.EOF 100 | } 101 | 102 | return gtid, nil 103 | } 104 | -------------------------------------------------------------------------------- /pkg/testutil/gomegamatcher/gomegamatcher.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2020 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package gomegamatcher 18 | 19 | import ( 20 | // nolint: golint,stylecheck 21 | . "github.com/onsi/gomega" 22 | // nolint: golint,stylecheck 23 | . "github.com/onsi/gomega/gstruct" 24 | 25 | gomegatypes "github.com/onsi/gomega/types" 26 | corev1 "k8s.io/api/core/v1" 27 | ) 28 | 29 | // HaveCondition is a helper func that returns 30 | func HaveCondition(condType interface{}, status corev1.ConditionStatus) gomegatypes.GomegaMatcher { 31 | return PointTo(MatchFields(IgnoreExtras, Fields{ 32 | "Status": MatchFields(IgnoreExtras, Fields{ 33 | "Conditions": ContainElement(MatchFields(IgnoreExtras, Fields{ 34 | "Type": Equal(condType), 35 | "Status": Equal(status), 36 | })), 37 | }), 38 | })) 39 | } 40 | -------------------------------------------------------------------------------- /pkg/util/util.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package util 18 | 19 | import ( 20 | "crypto/rand" 21 | "encoding/base64" 22 | mrand "math/rand" 23 | "time" 24 | ) 25 | 26 | func init() { 27 | mrand.Seed(time.Now().UnixNano()) 28 | } 29 | 30 | // RandomString returns a string by specified length of random chars(base64) 31 | func RandomString(length int) string { 32 | buf := make([]byte, length) 33 | if _, err := rand.Read(buf); err != nil { 34 | panic(err) 35 | } 36 | return base64.StdEncoding.EncodeToString(buf) 37 | } 38 | 39 | func randStringFrom(chars []rune, n int) string { 40 | b := make([]rune, n) 41 | for i := range b { 42 | b[i] = chars[mrand.Intn(len(chars))] 43 | } 44 | return string(b) 45 | } 46 | 47 | var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") 48 | 49 | // RandStringUser returns a random string of specififed length that contains only letters 50 | func RandStringUser(n int) string { 51 | return randStringFrom(letterRunes, n) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | var ( 4 | // NOTE: The $Format strings are replaced during 'git archive' thanks to the 5 | // companion .gitattributes file containing 'export-subst' in this same 6 | // directory. See also https://git-scm.com/docs/gitattributes 7 | gitVersion = "unreleased" // "v0.0.0-master+37c641098" 8 | gitCommit = "" // sha1 from git, output of $(git rev-parse HEAD) 9 | gitTreeState = "" // state of git tree, either "clean" or "dirty" 10 | 11 | buildDate = "" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') 12 | environment = "local" 13 | ) 14 | 15 | // Info represents metadata about current running instance 16 | type Info struct { 17 | BuildDate string `json:"BUILD_DATE"` 18 | Environment string `json:"ENVIRONMENT"` 19 | GitCommit string `json:"GIT_COMMIT"` 20 | GitTreeState string `json:"GIT_TREE_STATE"` 21 | GitVersion string `json:"GIT_VERSION"` 22 | } 23 | 24 | // GetInfo is a helper function that retrieves metadata about the currently running instance 25 | func GetInfo() Info { 26 | return Info{ 27 | GitVersion: gitVersion, 28 | GitCommit: gitCommit, 29 | GitTreeState: gitTreeState, 30 | BuildDate: buildDate, 31 | Environment: environment, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /skaffold.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: skaffold/v1beta7 2 | kind: Config 3 | build: 4 | artifacts: 5 | - image: quay.io/presslabs/mysql-operator 6 | docker: 7 | dockerfile: hack/development/Dockerfile.operator 8 | - image: quay.io/presslabs/mysql-operator-sidecar-mysql57 9 | docker: 10 | dockerfile: hack/development/Dockerfile.sidecar 11 | buildArgs: 12 | XTRABACKUP_PKG: percona-xtrabackup-24 13 | - image: quay.io/presslabs/mysql-operator-sidecar-mysql8 14 | docker: 15 | dockerfile: hack/development/Dockerfile.sidecar 16 | buildArgs: 17 | XTRABACKUP_PKG: percona-xtrabackup-80 18 | - image: quay.io/presslabs/mysql-operator-orchestrator 19 | docker: 20 | dockerfile: hack/development/Dockerfile.orchestrator 21 | local: 22 | push: true 23 | deploy: 24 | helm: 25 | releases: 26 | - name: test 27 | chartPath: charts/mysql-operator 28 | valuesFiles: 29 | - hack/development/dev-values.yaml 30 | values: 31 | image: quay.io/presslabs/mysql-operator 32 | sidecarImage: quay.io/presslabs/mysql-operator-sidecar-mysql57 33 | sidecarMysql8Image: quay.io/presslabs/mysql-operator-sidecar-mysql8 34 | orchestrator.image: quay.io/presslabs/mysql-operator-orchestrator 35 | -------------------------------------------------------------------------------- /test/e2e-values.yaml: -------------------------------------------------------------------------------- 1 | extraArgs: 2 | - --debug 3 | 4 | imagePullPolicy: IfNotPresent 5 | 6 | orchestrator: 7 | topologyPassword: password1 8 | -------------------------------------------------------------------------------- /test/e2e/e2e_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package e2e 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/bitpoke/mysql-operator/test/e2e/framework" 23 | 24 | // test sources 25 | _ "github.com/bitpoke/mysql-operator/test/e2e/backups" 26 | _ "github.com/bitpoke/mysql-operator/test/e2e/cluster" 27 | ) 28 | 29 | func init() { 30 | // framework.ViperizeFlags() 31 | testing.Init() 32 | framework.RegisterParseFlags() 33 | } 34 | 35 | func TestE2E(t *testing.T) { 36 | RunE2ETests(t) 37 | } 38 | -------------------------------------------------------------------------------- /test/e2e/framework/cleanup.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016 The Kubernetes Authors. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package framework 18 | 19 | import "sync" 20 | 21 | type CleanupActionHandle *int 22 | 23 | var cleanupActionsLock sync.Mutex 24 | var cleanupActions = map[CleanupActionHandle]func(){} 25 | 26 | // AddCleanupAction installs a function that will be called in the event of the 27 | // whole test being terminated. This allows arbitrary pieces of the overall 28 | // test to hook into SynchronizedAfterSuite(). 29 | func AddCleanupAction(fn func()) CleanupActionHandle { 30 | p := CleanupActionHandle(new(int)) 31 | cleanupActionsLock.Lock() 32 | defer cleanupActionsLock.Unlock() 33 | cleanupActions[p] = fn 34 | return p 35 | } 36 | 37 | // RemoveCleanupAction removes a function that was installed by 38 | // AddCleanupAction. 39 | func RemoveCleanupAction(p CleanupActionHandle) { 40 | cleanupActionsLock.Lock() 41 | defer cleanupActionsLock.Unlock() 42 | delete(cleanupActions, p) 43 | } 44 | 45 | // RunCleanupActions runs all functions installed by AddCleanupAction. It does 46 | // not remove them (see RemoveCleanupAction) but it does run unlocked, so they 47 | // may remove themselves. 48 | func RunCleanupActions() { 49 | list := []func(){} 50 | func() { 51 | cleanupActionsLock.Lock() 52 | defer cleanupActionsLock.Unlock() 53 | for _, fn := range cleanupActions { 54 | list = append(list, fn) 55 | } 56 | }() 57 | // Run unlocked. 58 | for _, fn := range list { 59 | fn() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /test/e2e/framework/ginkgowrapper/BUILD: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//visibility:public"]) 2 | 3 | load( 4 | "@io_bazel_rules_go//go:def.bzl", 5 | "go_library", 6 | ) 7 | 8 | go_library( 9 | name = "go_default_library", 10 | srcs = ["wrapper.go"], 11 | importpath = "k8s.io/kubernetes/test/e2e/framework/ginkgowrapper", 12 | deps = ["//vendor/github.com/onsi/ginkgo:go_default_library"], 13 | ) 14 | 15 | filegroup( 16 | name = "package-srcs", 17 | srcs = glob(["**"]), 18 | tags = ["automanaged"], 19 | visibility = ["//visibility:private"], 20 | ) 21 | 22 | filegroup( 23 | name = "all-srcs", 24 | srcs = [":package-srcs"], 25 | tags = ["automanaged"], 26 | ) 27 | -------------------------------------------------------------------------------- /test/e2e/framework/helm.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2018 Pressinfra SRL 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package framework 18 | 19 | import ( 20 | "strings" 21 | 22 | "os" 23 | "os/exec" 24 | 25 | . "github.com/onsi/gomega" 26 | ) 27 | 28 | func setImage(p, image string) []string { 29 | repositoryTag := strings.SplitN(image, ":", 2) 30 | 31 | ret := []string{ 32 | "--set-string", p + ".repository=" + repositoryTag[0], 33 | } 34 | 35 | if len(repositoryTag) == 2 { 36 | ret = append(ret, "--set-string", p+".tag="+repositoryTag[1]) 37 | } 38 | 39 | return ret 40 | } 41 | 42 | func HelmInstallChart(release, ns string) { 43 | args := []string{ 44 | "install", release, "./" + TestContext.ChartPath, 45 | "--namespace", ns, 46 | "--values", TestContext.ChartValues, "--wait", 47 | "--kube-context", TestContext.KubeContext, 48 | } 49 | args = append(args, setImage("image", TestContext.OperatorImage)...) 50 | args = append(args, setImage("sidecar57.image", TestContext.SidecarMysql57Image)...) 51 | args = append(args, setImage("sidecar80.image", TestContext.SidecarMysql8Image)...) 52 | args = append(args, setImage("orchestrator.image", TestContext.OrchestratorImage)...) 53 | 54 | cmd := exec.Command("helm", args...) 55 | cmd.Stdout = os.Stdout 56 | cmd.Stderr = os.Stderr 57 | 58 | Expect(cmd.Run()).Should(Succeed()) 59 | } 60 | 61 | func HelmPurgeRelease(release, ns string) { 62 | args := []string{ 63 | "delete", release, 64 | "--namespace", ns, 65 | "--kube-context", TestContext.KubeContext, 66 | } 67 | cmd := exec.Command("helm", args...) 68 | cmd.Stdout = os.Stdout 69 | cmd.Stderr = os.Stderr 70 | 71 | Expect(cmd.Run()).Should(Succeed()) 72 | } 73 | -------------------------------------------------------------------------------- /test/e2e/framework/portforward/portforward.go: -------------------------------------------------------------------------------- 1 | package portforward 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "net" 8 | "net/http" 9 | "strconv" 10 | 11 | "k8s.io/client-go/rest" 12 | "k8s.io/client-go/tools/portforward" 13 | "k8s.io/client-go/transport/spdy" 14 | ) 15 | 16 | type Tunnel struct { 17 | Local int 18 | Remote int 19 | Namespace string 20 | PodName string 21 | Out io.Writer 22 | stopChan chan struct{} 23 | readyChan chan struct{} 24 | config *rest.Config 25 | client rest.Interface 26 | } 27 | 28 | func NewTunnel(client rest.Interface, config *rest.Config, namespace, podName string, remote int) *Tunnel { 29 | return &Tunnel{ 30 | config: config, 31 | client: client, 32 | Namespace: namespace, 33 | PodName: podName, 34 | Remote: remote, 35 | stopChan: make(chan struct{}, 1), 36 | readyChan: make(chan struct{}, 1), 37 | Out: ioutil.Discard, 38 | } 39 | } 40 | 41 | func (t *Tunnel) ForwardPort() error { 42 | u := t.client.Post(). 43 | Resource("pods"). 44 | Namespace(t.Namespace). 45 | Name(t.PodName). 46 | SubResource("portforward").URL() 47 | 48 | transport, upgrader, err := spdy.RoundTripperFor(t.config) 49 | if err != nil { 50 | return err 51 | } 52 | dialer := spdy.NewDialer(upgrader, &http.Client{Transport: transport}, "POST", u) 53 | 54 | local, err := getAvailablePort() 55 | if err != nil { 56 | return fmt.Errorf("could not find an available port: %s", err) 57 | } 58 | t.Local = local 59 | 60 | ports := []string{fmt.Sprintf("%d:%d", t.Local, t.Remote)} 61 | 62 | pf, err := portforward.New(dialer, ports, t.stopChan, t.readyChan, t.Out, t.Out) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | errChan := make(chan error) 68 | go func() { 69 | errChan <- pf.ForwardPorts() 70 | }() 71 | 72 | select { 73 | case err = <-errChan: 74 | return fmt.Errorf("forwarding ports: %v", err) 75 | case <-pf.Ready: 76 | return nil 77 | } 78 | } 79 | 80 | func (t *Tunnel) Close() { 81 | close(t.stopChan) 82 | } 83 | 84 | func getAvailablePort() (int, error) { 85 | l, err := net.Listen("tcp", ":0") 86 | if err != nil { 87 | return 0, err 88 | } 89 | defer l.Close() 90 | 91 | _, p, err := net.SplitHostPort(l.Addr().String()) 92 | if err != nil { 93 | return 0, err 94 | } 95 | port, err := strconv.Atoi(p) 96 | if err != nil { 97 | return 0, err 98 | } 99 | return port, err 100 | } 101 | --------------------------------------------------------------------------------