Compare commits

...

52 Commits

Author SHA1 Message Date
dependabot[bot]
197f1c1a8d chore(deps): bump golang from 1.25.1 to 1.25.5
Bumps golang from 1.25.1 to 1.25.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-08 09:14:52 +00:00
8ac6ffc394 Merge pull request #197 from jkaninda/dependabot/go_modules/github.com/jkaninda/go-utils-0.1.3
Some checks failed
Lint / Run on Ubuntu (push) Has been cancelled
Tests / test (push) Has been cancelled
chore(deps): bump github.com/jkaninda/go-utils from 0.1.1 to 0.1.3
2025-09-10 05:56:13 +02:00
59296c0c17 Merge branch 'main' into dependabot/go_modules/github.com/jkaninda/go-utils-0.1.3 2025-09-10 05:55:13 +02:00
19b64bbad1 Merge pull request #198 from jkaninda/dependabot/docker/golang-1.25.1
chore(deps): bump golang from 1.24.6 to 1.25.1
2025-09-10 05:54:28 +02:00
640e11b924 Merge pull request #199 from jkaninda/dependabot/go_modules/github.com/spf13/cobra-1.10.1
chore(deps): bump github.com/spf13/cobra from 1.9.1 to 1.10.1
2025-09-10 05:54:15 +02:00
dependabot[bot]
a746366348 chore(deps): bump github.com/spf13/cobra from 1.9.1 to 1.10.1
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.9.1 to 1.10.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.9.1...v1.10.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-version: 1.10.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 09:25:00 +00:00
dependabot[bot]
64d57a077e chore(deps): bump golang from 1.24.6 to 1.25.1
Bumps golang from 1.24.6 to 1.25.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.25.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-08 09:21:49 +00:00
dependabot[bot]
995db83b7c chore(deps): bump github.com/jkaninda/go-utils from 0.1.1 to 0.1.3
Bumps [github.com/jkaninda/go-utils](https://github.com/jkaninda/go-utils) from 0.1.1 to 0.1.3.
- [Release notes](https://github.com/jkaninda/go-utils/releases)
- [Commits](https://github.com/jkaninda/go-utils/compare/v0.1.1...v0.1.3)

---
updated-dependencies:
- dependency-name: github.com/jkaninda/go-utils
  dependency-version: 0.1.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-25 17:37:38 +00:00
5d5cbea60b Merge pull request #194 from jkaninda/dependabot/docker/alpine-3.22.1
Some checks failed
Lint / Run on Ubuntu (push) Has been cancelled
Tests / test (push) Has been cancelled
chore(deps): bump alpine from 3.22.0 to 3.22.1
2025-08-13 04:45:07 +02:00
dependabot[bot]
2cb5603b88 chore(deps): bump alpine from 3.22.0 to 3.22.1
Some checks failed
Lint / Run on Ubuntu (push) Has been cancelled
Bumps alpine from 3.22.0 to 3.22.1.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 3.22.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 18:28:38 +00:00
548de725dd Merge pull request #195 from jkaninda/dependabot/docker/golang-1.24.6
Some checks failed
Lint / Run on Ubuntu (push) Has been cancelled
Tests / test (push) Has been cancelled
chore(deps): bump golang from 1.24.5 to 1.24.6
2025-08-11 20:24:27 +02:00
dependabot[bot]
37b2867974 chore(deps): bump golang from 1.24.5 to 1.24.6
Bumps golang from 1.24.5 to 1.24.6.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-11 14:49:26 +00:00
d6c01c9a4a Merge pull request #193 from jkaninda/dependabot/docker/golang-1.24.5
Some checks failed
Lint / Run on Ubuntu (push) Has been cancelled
Tests / test (push) Has been cancelled
chore(deps): bump golang from 1.24.4 to 1.24.5
2025-07-19 08:00:22 +02:00
dependabot[bot]
bd92cc6844 chore(deps): bump golang from 1.24.4 to 1.24.5
Bumps golang from 1.24.4 to 1.24.5.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-14 12:14:33 +00:00
3006abac3e Merge pull request #192 from jkaninda/dependabot/docker/golang-1.24.4
Some checks failed
Tests / test (push) Has been cancelled
Lint / Run on Ubuntu (push) Has been cancelled
chore(deps): bump golang from 1.24.3 to 1.24.4
2025-06-09 19:53:25 +02:00
dependabot[bot]
3a16f6929c chore(deps): bump golang from 1.24.3 to 1.24.4
Bumps golang from 1.24.3 to 1.24.4.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-09 10:17:20 +00:00
2f3ed7d0d8 Merge pull request #191 from jkaninda/dependabot/docker/alpine-3.22.0
chore(deps): bump alpine from 3.21.3 to 3.22.0
2025-06-06 20:01:27 +02:00
dependabot[bot]
a92bba05e4 chore(deps): bump alpine from 3.21.3 to 3.22.0
Bumps alpine from 3.21.3 to 3.22.0.

---
updated-dependencies:
- dependency-name: alpine
  dependency-version: 3.22.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-02 11:09:07 +00:00
7b565d54bd Merge pull request #190 from jkaninda/dependabot/docker/golang-1.24.3
Some checks failed
Lint / Run on Ubuntu (push) Successful in 18m52s
Tests / test (push) Failing after 1m14s
chore(deps): bump golang from 1.24.2 to 1.24.3
2025-05-14 23:03:58 +02:00
dependabot[bot]
d10321dac6 chore(deps): bump golang from 1.24.2 to 1.24.3
Bumps golang from 1.24.2 to 1.24.3.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-12 09:54:31 +00:00
d151c50caa Merge pull request #189 from jkaninda/nightly
Some checks failed
Tests / test (push) Has been cancelled
Lint / Run on Ubuntu (push) Has been cancelled
Nightly
2025-05-11 20:10:05 +02:00
07ecec57b3 Add why use MySQL-BKUP 2025-05-11 20:09:19 +02:00
bc4aab6ed0 Add Docker labels 2025-05-11 20:05:16 +02:00
12c17c18d6 doc: update key features (#188)
Some checks failed
Tests / test (push) Has been cancelled
Deploy Documenation site to GitHub Pages / build (push) Has been cancelled
Lint / Run on Ubuntu (push) Has been cancelled
Deploy Documenation site to GitHub Pages / deploy (push) Has been cancelled
2025-05-10 17:47:12 +02:00
80cd70e153 Merge pull request #187 from jkaninda/dependabot/docker/golang-1.24.2
Some checks failed
Lint / Run on Ubuntu (push) Successful in 18m40s
Tests / test (push) Failing after 25s
chore(deps): bump golang from 1.24.1 to 1.24.2
2025-04-08 19:29:23 +02:00
dependabot[bot]
80abdb4299 chore(deps): bump golang from 1.24.1 to 1.24.2
Bumps golang from 1.24.1 to 1.24.2.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.24.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-07 10:37:49 +00:00
72b21f00e6 Merge pull request #186 from jkaninda/nightly
Some checks failed
Lint / Run on Ubuntu (push) Successful in 18m44s
Tests / test (push) Failing after 18s
Nightly
2025-03-17 09:09:09 +01:00
3439c74257 Merge branch 'main' of github.com:jkaninda/mysql-bkup into nightly 2025-03-17 09:05:24 +01:00
4110ff4e64 Merge branch 'docs' into nightly 2025-03-17 09:05:16 +01:00
cb299a35bf enh: enhancement of logging 2025-03-17 09:05:04 +01:00
ba7c096bf3 Merge pull request #185 from jkaninda/docs
Some checks failed
Deploy Documenation site to GitHub Pages / build (push) Failing after 9m27s
Deploy Documenation site to GitHub Pages / deploy (push) Has been skipped
Lint / Run on Ubuntu (push) Successful in 18m44s
Tests / test (push) Failing after 20s
doc: update deployment examples
2025-03-16 11:50:32 +01:00
6ac8dcef9e doc: update deployment examples
All checks were successful
Lint / Run on Ubuntu (push) Successful in 18m39s
2025-03-16 11:49:36 +01:00
6bcc5d6bd4 Merge pull request #184 from jkaninda/docs
Some checks failed
Deploy Documenation site to GitHub Pages / build (push) Failing after 9m27s
Deploy Documenation site to GitHub Pages / deploy (push) Has been skipped
Lint / Run on Ubuntu (push) Successful in 18m36s
Tests / test (push) Failing after 18s
doc: update configuration deployment
2025-03-16 05:55:14 +01:00
fd35fabf97 doc: update configuration deployment 2025-03-16 05:45:12 +01:00
23cce10e8c Merge branch 'main' of github.com:jkaninda/mysql-bkup into nightly 2025-03-16 05:37:34 +01:00
e666466d27 fix: database name not set error when using flag -d (#183)
Some checks failed
Lint / Run on Ubuntu (push) Successful in 18m39s
Tests / test (push) Failing after 13s
* fix: database name not set error when using flag -d
2025-03-14 14:38:06 +01:00
36bca254a9 ci: Set -d flag for database name
Some checks failed
Build / docker (push) Failing after 8s
Lint / Run on Ubuntu (push) Successful in 18m37s
Tests / test (push) Failing after 1m0s
2025-03-14 14:34:25 +01:00
ad18d42145 fix: database name not set error when using flag -d 2025-03-14 14:32:38 +01:00
d9d44c2798 Merge pull request #182 from jkaninda/nightly
Some checks failed
Deploy Documenation site to GitHub Pages / build (push) Failing after 9m29s
Deploy Documenation site to GitHub Pages / deploy (push) Has been skipped
Lint / Run on Ubuntu (push) Successful in 18m40s
Tests / test (push) Failing after 2m19s
Nightly
2025-03-14 09:59:33 +01:00
300a592508 Merge branch 'main' of github.com:jkaninda/mysql-bkup into nightly
Some checks failed
Build / docker (push) Failing after 9s
Lint / Run on Ubuntu (push) Successful in 18m40s
Tests / test (push) Failing after 19s
2025-03-14 09:58:47 +01:00
be82e841e7 ci: set docker tests on main 2025-03-14 09:58:41 +01:00
a73a365ebf ci: set docker tests on main 2025-03-14 09:57:59 +01:00
75e965c0c5 Merge pull request #181 from jkaninda/nightly
doc: update reference
2025-03-14 09:55:02 +01:00
fc60ddb308 doc: update reference 2025-03-14 09:53:38 +01:00
573ef15ef3 Merge pull request #180 from jkaninda/nightly
ci: update  Github pages action
2025-03-14 09:50:49 +01:00
b1776d3689 ci: update Github pages action 2025-03-14 09:50:13 +01:00
376d47f738 Merge pull request #178 from jkaninda/nightly
feat: add backup all databases separately
2025-03-14 09:43:43 +01:00
eb6268f8ec ci: add Docker tests (#179) 2025-03-14 09:41:37 +01:00
731e2d789d ci: add go lint 2025-03-14 05:24:46 +01:00
6300a8f2dd feat: add backup all databases 2025-03-14 05:20:54 +01:00
002c93a796 Merge pull request #176 from jkaninda/dependabot/docker/golang-1.24.1
chore(deps): bump golang from 1.24.0 to 1.24.1
2025-03-12 16:29:14 +01:00
dependabot[bot]
907e70d552 chore(deps): bump golang from 1.24.0 to 1.24.1
Bumps golang from 1.24.0 to 1.24.1.

---
updated-dependencies:
- dependency-name: golang
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-10 09:25:40 +00:00
24 changed files with 872 additions and 235 deletions

View File

@@ -32,14 +32,14 @@ jobs:
working-directory: docs working-directory: docs
- name: Setup Pages - name: Setup Pages
id: pages id: pages
uses: actions/configure-pages@v2 uses: actions/configure-pages@v5
- name: Build with Jekyll - name: Build with Jekyll
working-directory: docs working-directory: docs
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
env: env:
JEKYLL_ENV: production JEKYLL_ENV: production
- name: Upload artifact - name: Upload artifact
uses: actions/upload-pages-artifact@v1 uses: actions/upload-pages-artifact@v3
with: with:
path: 'docs/_site/' path: 'docs/_site/'
@@ -52,4 +52,4 @@ jobs:
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v1 uses: actions/deploy-pages@v4

23
.github/workflows/lint.yml vendored Normal file
View File

@@ -0,0 +1,23 @@
name: Lint
on:
push:
pull_request:
jobs:
lint:
name: Run on Ubuntu
runs-on: ubuntu-latest
steps:
- name: Clone the code
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '~1.23'
- name: Run linter
uses: golangci/golangci-lint-action@v6
with:
version: v1.61

289
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,289 @@
name: Tests
on:
push:
branches:
- main
- nightly
pull_request:
branches:
- main
env:
IMAGE_NAME: mysql-bkup
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:9
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testdb
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -uuser -ppassword"
--health-interval=10s
--health-timeout=5s
--health-retries=5
mysql8:
image: mysql:8
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testdb
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- 3308:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -uuser -ppassword"
--health-interval=10s
--health-timeout=5s
--health-retries=5
mysql5:
image: mysql:5
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: testdb
MYSQL_USER: user
MYSQL_PASSWORD: password
ports:
- 3305:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1 -uuser -ppassword"
--health-interval=10s
--health-timeout=5s
--health-retries=5
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Create Minio container
run: |
docker run -d --rm --name minio \
--network host \
-p 9000:9000 \
-e MINIO_ACCESS_KEY=minioadmin \
-e MINIO_SECRET_KEY=minioadmin \
-e MINIO_REGION_NAME="eu" \
minio/minio server /data
echo "Create Minio container completed"
- name: Install MinIO Client (mc)
run: |
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc
sudo mv mc /usr/local/bin/
- name: Wait for MinIO to be ready
run: sleep 5
- name: Configure MinIO Client
run: |
mc alias set local http://localhost:9000 minioadmin minioadmin
mc alias list
- name: Create MinIO Bucket
run: |
mc mb local/backups
echo "Bucket backups created successfully."
# Build the Docker image
- name: Build Docker Image
run: |
docker buildx build --build-arg appVersion=test -t ${{ env.IMAGE_NAME }}:latest --load .
- name: Verify Docker images
run: |
docker images
- name: Wait for MySQL to be ready
run: |
docker run --rm --network host mysql:9 mysqladmin ping -h 127.0.0.1 -uuser -ppassword --wait
- name: Test restore
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=root \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest restore -f init.sql
echo "Database restore completed"
- name: Test restore Mysql8
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_PORT=3308 \
-e DB_USERNAME=root \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest restore -f init.sql
echo "Test restore Mysql8 completed"
- name: Test restore Mysql5
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_PORT=3305 \
-e DB_USERNAME=root \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest restore -f init.sql
echo "Test restore Mysql5 completed"
- name: Test backup
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=user \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest backup
echo "Database backup completed"
- name: Test backup Mysql8
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_PORT=3308 \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=user \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest backup
echo "Test backup Mysql8 completed"
- name: Test backup Mysql5
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_PORT=3305 \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=user \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest backup
echo "Test backup Mysql5 completed"
- name: Test encrypted backup
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=user \
-e DB_PASSWORD=password \
-e GPG_PASSPHRASE=password \
${{ env.IMAGE_NAME }}:latest backup -d testdb --disable-compression --custom-name encrypted-bkup
echo "Database encrypted backup completed"
- name: Test restore encrypted backup | testdb -> testdb2
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=root \
-e DB_PASSWORD=password \
-e GPG_PASSPHRASE=password \
-e DB_NAME=testdb2 \
${{ env.IMAGE_NAME }}:latest restore -f /backup/encrypted-bkup.sql.gpg
echo "Test restore encrypted backup completed"
- name: Test migrate database testdb -> testdb3
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=root \
-e DB_PASSWORD=password \
-e GPG_PASSPHRASE=password \
-e DB_NAME=testdb \
-e TARGET_DB_HOST=127.0.0.1 \
-e TARGET_DB_PORT=3306 \
-e TARGET_DB_NAME=testdb3 \
-e TARGET_DB_USERNAME=root \
-e TARGET_DB_PASSWORD=password \
${{ env.IMAGE_NAME }}:latest migrate
echo "Test migrate database testdb -> testdb3 completed"
- name: Test backup all databases
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=root \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest backup --all-databases
echo "Database backup completed"
- name: Test multiple backup
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e TESTDB2_DB_USERNAME=root \
-e TESTDB2_DB_PASSWORD=password \
-e TESTDB2_DB_HOST=127.0.0.1 \
${{ env.IMAGE_NAME }}:latest backup -c /backup/test_config.yaml
echo "Database backup completed"
- name: Test backup Minio (s3)
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=user \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
-e AWS_S3_ENDPOINT="http://127.0.0.1:9000" \
-e AWS_S3_BUCKET_NAME=backups \
-e AWS_ACCESS_KEY=minioadmin \
-e AWS_SECRET_KEY=minioadmin \
-e AWS_DISABLE_SSL="true" \
-e AWS_REGION="eu" \
-e AWS_FORCE_PATH_STYLE="true" ${{ env.IMAGE_NAME }}:latest backup -s s3 --custom-name minio-backup
echo "Test backup Minio (s3) completed"
- name: Test restore Minio (s3)
run: |
docker run --rm --name ${{ env.IMAGE_NAME }} \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=user \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
-e AWS_S3_ENDPOINT="http://127.0.0.1:9000" \
-e AWS_S3_BUCKET_NAME=backups \
-e AWS_ACCESS_KEY=minioadmin \
-e AWS_SECRET_KEY=minioadmin \
-e AWS_DISABLE_SSL="true" \
-e AWS_REGION="eu" \
-e AWS_FORCE_PATH_STYLE="true" ${{ env.IMAGE_NAME }}:latest restore -s s3 -f minio-backup.sql.gz
echo "Test backup Minio (s3) completed"
- name: Test scheduled backup
run: |
docker run -d --rm --name ${{ env.IMAGE_NAME }} \
-v ./migrations:/backup/ \
--network host \
-e DB_HOST=127.0.0.1 \
-e DB_USERNAME=user \
-e DB_PASSWORD=password \
-e DB_NAME=testdb \
${{ env.IMAGE_NAME }}:latest backup -e "@every 10s"
echo "Waiting for backup to be done..."
sleep 25
docker logs ${{ env.IMAGE_NAME }}
echo "Test scheduled backup completed"
# Cleanup: Stop and remove containers
- name: Clean up
run: |
docker stop ${{ env.IMAGE_NAME }} || true
docker rm ${{ env.IMAGE_NAME }} || true

View File

@@ -1,4 +1,4 @@
FROM golang:1.24.0 AS build FROM golang:1.25.5 AS build
WORKDIR /app WORKDIR /app
ARG appVersion="" ARG appVersion=""
@@ -10,7 +10,7 @@ RUN go mod download
# Build # Build
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-X 'github.com/jkaninda/mysql-bkup/utils.Version=${appVersion}'" -o /app/mysql-bkup RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-X 'github.com/jkaninda/mysql-bkup/utils.Version=${appVersion}'" -o /app/mysql-bkup
FROM alpine:3.21.3 FROM alpine:3.22.1
ENV TZ=UTC ENV TZ=UTC
ARG WORKDIR="/config" ARG WORKDIR="/config"
ARG BACKUPDIR="/backup" ARG BACKUPDIR="/backup"
@@ -18,9 +18,12 @@ ARG BACKUP_TMP_DIR="/tmp/backup"
ARG TEMPLATES_DIR="/config/templates" ARG TEMPLATES_DIR="/config/templates"
ARG appVersion="" ARG appVersion=""
ENV VERSION=${appVersion} ENV VERSION=${appVersion}
LABEL author="Jonas Kaninda" LABEL org.opencontainers.image.title="mysql-bkup"
LABEL version=${appVersion} LABEL org.opencontainers.image.description="A lightweight MySQL backup and restore tool"
LABEL github="github.com/jkaninda/mysql-bkup" LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.authors="Jonas Kaninda <me@jonaskaninda.com>"
LABEL org.opencontainers.image.version=${appVersion}
LABEL org.opencontainers.image.source="https://github.com/jkaninda/mysql-bkup"
RUN apk --update add --no-cache mysql-client mariadb-connector-c tzdata ca-certificates RUN apk --update add --no-cache mysql-client mariadb-connector-c tzdata ca-certificates
RUN mkdir -p $WORKDIR $BACKUPDIR $TEMPLATES_DIR $BACKUP_TMP_DIR && \ RUN mkdir -p $WORKDIR $BACKUPDIR $TEMPLATES_DIR $BACKUP_TMP_DIR && \

255
README.md
View File

@@ -3,6 +3,14 @@
**MYSQL-BKUP** is a Docker container image designed to **backup, restore, and migrate MySQL databases**. **MYSQL-BKUP** is a Docker container image designed to **backup, restore, and migrate MySQL databases**.
It supports a variety of storage options and ensures data security through GPG encryption. It supports a variety of storage options and ensures data security through GPG encryption.
MYSQL-BKUP is designed for seamless deployment on **Docker** and **Kubernetes**, simplifying MySQL backup, restoration, and migration across environments.
It is a lightweight, multi-architecture solution compatible with **Docker**, **Docker Swarm**, **Kubernetes**, and other container orchestration platforms.
[![Tests](https://github.com/jkaninda/mysql-bkup/actions/workflows/tests.yml/badge.svg)](https://github.com/jkaninda/mysql-bkup/actions/workflows/tests.yml)
[![Build](https://github.com/jkaninda/mysql-bkup/actions/workflows/release.yml/badge.svg)](https://github.com/jkaninda/mysql-bkup/actions/workflows/release.yml) [![Build](https://github.com/jkaninda/mysql-bkup/actions/workflows/release.yml/badge.svg)](https://github.com/jkaninda/mysql-bkup/actions/workflows/release.yml)
[![Go Report](https://goreportcard.com/badge/github.com/jkaninda/mysql-bkup)](https://goreportcard.com/report/github.com/jkaninda/mysql-bkup) [![Go Report](https://goreportcard.com/badge/github.com/jkaninda/mysql-bkup)](https://goreportcard.com/report/github.com/jkaninda/mysql-bkup)
![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/jkaninda/mysql-bkup?style=flat-square) ![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/jkaninda/mysql-bkup?style=flat-square)
@@ -11,9 +19,9 @@ It supports a variety of storage options and ensures data security through GPG e
## Features ## Features
- **Storage Options:** - **Flexible Storage Backends:**
- Local storage - Local filesystem
- AWS S3 or any S3-compatible object storage - Amazon S3 & S3-compatible storage (e.g., MinIO, Wasabi)
- FTP - FTP
- SSH-compatible storage - SSH-compatible storage
- Azure Blob storage - Azure Blob storage
@@ -33,16 +41,19 @@ It supports a variety of storage options and ensures data security through GPG e
- **Telegram** - **Telegram**
- **Email** - **Email**
## Use Cases ## 💡Use Cases
- **Automated Recurring Backups:** Schedule regular backups for MySQL databases. - **Scheduled Backups**: Automate recurring backups using Docker or Kubernetes.
- **Cross-Environment Migration:** Easily migrate your MySQL databases across different environments using supported storage options. - **Disaster Recovery:** Quickly restore backups to a clean MySQL instance.
- **Secure Backup Management:** Protect your data with GPG encryption. - **Database Migration**: Seamlessly move data across environments using the built-in `migrate` feature.
- **Secure Archiving:** Keep backups encrypted and safely stored in the cloud or remote servers.
Successfully tested on: ## ✅ Verified Platforms:
MYSQL-BKUP has been tested and runs successfully on:
- Docker - Docker
- Docker in Swarm mode - Docker Swarm
- Kubernetes - Kubernetes
- OpenShift - OpenShift
@@ -58,58 +69,75 @@ Successfully tested on:
- [PostgreSQL](https://github.com/jkaninda/pg-bkup) - [PostgreSQL](https://github.com/jkaninda/pg-bkup)
## Storage:
- Local
- AWS S3 or any S3 Alternatives for Object Storage
- SSH remote storage server
- FTP remote storage server
- Azure Blob storage
## Quickstart ## Quickstart
### Simple backup using Docker CLI ### Simple Backup Using Docker CLI
To run a one time backup, bind your local volume to `/backup` in the container and run the `backup` command: To perform a one-time backup, bind your local volume to `/backup` in the container and run the `backup` command:
```shell ```shell
docker run --rm --network your_network_name \ docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \ -v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \ -e "DB_HOST=dbhost" \
-e "DB_PORT=3306" \ -e "DB_PORT=3306" \
-e "DB_USERNAME=username" \ -e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \ -e "DB_PASSWORD=password" \
jkaninda/mysql-bkup backup -d database_name jkaninda/mysql-bkup backup -d database_name
``` ```
Alternatively, pass a `--env-file` in order to use a full config as described below. Alternatively, use an environment file (`--env-file`) for configuration:
```yaml
docker run --rm --network your_network_name \
--env-file your-env-file \
-v $PWD/backup:/backup/ \
jkaninda/mysql-bkup backup -d database_name
```
### Simple restore using Docker CLI
To restore a database, bind your local volume to `/backup` in the container and run the `restore` command:
```shell ```shell
docker run --rm --network your_network_name \ docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \ --env-file your-env-file \
-e "DB_HOST=dbhost" \ -v $PWD/backup:/backup/ \
-e "DB_PORT=3306" \ jkaninda/mysql-bkup backup -d database_name
-e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \
jkaninda/mysql-bkup restore -d database_name -f backup_file.sql.gz
``` ```
### Simple backup in docker compose file
### Backup All Databases
To back up all databases on the server, use the `--all-databases` or `-a` flag. By default, this creates individual backup files for each database.
```shell
docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \
-e "DB_PORT=3306" \
-e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \
jkaninda/mysql-bkup backup --all-databases --disable-compression
```
> **Note:** Use the `--all-in-one` or `-A` flag to combine backups into a single file.
---
### Simple Restore Using Docker CLI
To restore a database, bind your local volume to `/backup` and run the `restore` command:
```shell
docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \
-e "DB_PORT=3306" \
-e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \
jkaninda/mysql-bkup restore -d database_name -f backup_file.sql.gz
```
---
### Backup with Docker Compose
Below is an example of a `docker-compose.yml` file for running a one-time backup:
```yaml ```yaml
services: services:
mysql-bkup: pg-bkup:
# In production, it is advised to lock your image tag to a proper # In production, pin your image tag to a specific release version instead of `latest`.
# release version instead of using `latest`. # See available releases: https://github.com/jkaninda/mysql-bkup/releases
# Check https://github.com/jkaninda/mysql-bkup/releases
# for a list of available releases.
image: jkaninda/mysql-bkup image: jkaninda/mysql-bkup
container_name: mysql-bkup container_name: mysql-bkup
command: backup command: backup
@@ -122,29 +150,39 @@ services:
- DB_USERNAME=bar - DB_USERNAME=bar
- DB_PASSWORD=password - DB_PASSWORD=password
- TZ=Europe/Paris - TZ=Europe/Paris
# mysql-bkup container must be connected to the same network with your database
networks: networks:
- web - web
networks: networks:
web: web:
``` ```
### Docker recurring backup
---
### Recurring Backups with Docker
You can schedule recurring backups using the `--cron-expression` or `-e` flag:
```shell ```shell
docker run --rm --network network_name \ docker run --rm --network network_name \
-v $PWD/backup:/backup/ \ -v $PWD/backup:/backup/ \
-e "DB_HOST=hostname" \ -e "DB_HOST=hostname" \
-e "DB_USERNAME=user" \ -e "DB_USERNAME=user" \
-e "DB_PASSWORD=password" \ -e "DB_PASSWORD=password" \
jkaninda/mysql-bkup backup -d dbName --cron-expression "@every 15m" #@midnight jkaninda/mysql-bkup backup -d dbName --cron-expression "@every 15m"
``` ```
See: https://jkaninda.github.io/mysql-bkup/reference/#predefined-schedules
For predefined schedules, refer to the [documentation](https://jkaninda.github.io/mysql-bkup/reference/#predefined-schedules).
---
## Deploy on Kubernetes ## Deploy on Kubernetes
For Kubernetes, you don't need to run it in scheduled mode. You can deploy it as Job or CronJob. For Kubernetes, you can deploy `mysql-bkup` as a Job or CronJob. Below are examples for both.
### Simple Kubernetes backup Job : ### Kubernetes Backup Job
This example defines a one-time backup job:
```yaml ```yaml
apiVersion: batch/v1 apiVersion: batch/v1
@@ -157,15 +195,10 @@ spec:
spec: spec:
containers: containers:
- name: mysql-bkup - name: mysql-bkup
# In production, it is advised to lock your image tag to a proper # Pin the image tag to a specific release version in production.
# release version instead of using `latest`. # See available releases: https://github.com/jkaninda/mysql-bkup/releases
# Check https://github.com/jkaninda/mysql-bkup/releases
# for a list of available releases.
image: jkaninda/mysql-bkup image: jkaninda/mysql-bkup
command: command: ["backup", "-d", "dbname"]
- /bin/sh
- -c
- backup -d dbname
resources: resources:
limits: limits:
memory: "128Mi" memory: "128Mi"
@@ -183,10 +216,94 @@ spec:
volumes: volumes:
- name: backup - name: backup
hostPath: hostPath:
path: /home/toto/backup # directory location on host path: /home/toto/backup # Directory location on the host
type: Directory # this field is optional type: Directory # Optional field
restartPolicy: Never restartPolicy: Never
``` ```
### Kubernetes CronJob for Scheduled Backups
For scheduled backups, use a `CronJob`:
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-bkup-cronjob
spec:
schedule: "0 2 * * *" # Runs daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: pg-bkup
image: jkaninda/mysql-bkup
command: ["backup", "-d", "dbname"]
env:
- name: DB_HOST
value: "mysql"
- name: DB_USERNAME
value: "user"
- name: DB_PASSWORD
value: "password"
volumeMounts:
- mountPath: /backup
name: backup
volumes:
- name: backup
hostPath:
path: /home/toto/backup
type: Directory
restartPolicy: OnFailure
```
---
## 🚀 Why Use MYSQL-BKUP?
**MYSQL-BKUP** isn't just another MySQL backup tool, it's a robust, production-ready solution purpose-built for modern DevOps workflows.
Heres why developers, sysadmins, and DevOps choose **MYSQL-BKUP**:
### ✅ All-in-One Backup, Restore & Migration
Whether you're backing up a single database, restoring critical data, or migrating across environments, MYSQL-BKUP handles it all with a **single, unified CLI** no scripting gymnastics required.
### 🔄 Works Everywhere You Deploy
Designed to be cloud-native:
* **Runs seamlessly on Docker, Docker Swarm, and Kubernetes**
* Supports **CronJobs** for automated scheduled backups
* Compatible with GitOps and CI/CD workflows
### ☁️ Flexible Storage Integrations
Store your backups **anywhere**:
* Local disks
* Amazon S3, MinIO, Wasabi, Azure Blob, FTP, SSH
### 🔒 Enterprise-Grade Security
* **GPG Encryption**: Protect sensitive data with optional encryption before storing backups locally or in the cloud.
* **Secure Storage** Options: Supports S3, Azure Blob, SFTP, and SSH with encrypted transfers, keeping backups safe from unauthorized access.
### 📬 Instant Notifications
Stay in the loop with real-time notifications via **Telegram** and **Email**. Know immediately when a backup succeeds—or fails.
### 🏃‍♂️ Lightweight and Fast
Written in **Go**, MYSQL-BKUP is fast, multi-arch compatible (`amd64`, `arm64`, `arm/v7`), and optimized for minimal memory and CPU usage. Ideal for both cloud and edge deployments.
### 🧪 Tested. Verified. Trusted.
Actively maintained with **automated testing**, **Docker image size optimizations**, and verified support across major container platforms.
---
## Available image registries ## Available image registries
This Docker image is published to both Docker Hub and the GitHub container registry. This Docker image is published to both Docker Hub and the GitHub container registry.

View File

@@ -50,7 +50,8 @@ func init() {
BackupCmd.PersistentFlags().StringP("cron-expression", "e", "", "Backup cron expression (e.g., `0 0 * * *` or `@daily`)") BackupCmd.PersistentFlags().StringP("cron-expression", "e", "", "Backup cron expression (e.g., `0 0 * * *` or `@daily`)")
BackupCmd.PersistentFlags().StringP("config", "c", "", "Configuration file for multi database backup. (e.g: `/backup/config.yaml`)") BackupCmd.PersistentFlags().StringP("config", "c", "", "Configuration file for multi database backup. (e.g: `/backup/config.yaml`)")
BackupCmd.PersistentFlags().BoolP("disable-compression", "", false, "Disable backup compression") BackupCmd.PersistentFlags().BoolP("disable-compression", "", false, "Disable backup compression")
BackupCmd.PersistentFlags().BoolP("all", "a", false, "Backup all databases") BackupCmd.PersistentFlags().BoolP("all-databases", "a", false, "Backup all databases")
BackupCmd.PersistentFlags().BoolP("single-file", "", false, "Backup all databases in a single file") BackupCmd.PersistentFlags().BoolP("all-in-one", "A", false, "Backup all databases in a single file")
BackupCmd.PersistentFlags().StringP("custom-name", "", "", "Custom backup name")
} }

View File

@@ -0,0 +1,61 @@
---
title: Backup all databases in the server
layout: default
parent: How Tos
nav_order: 12
---
# Backup All Databases
MySQL-Bkup supports backing up all databases on the server using the `--all-databases` (`-a`) flag. By default, this creates separate backup files for each database. If you prefer a single backup file, you can use the `--all-in-one` (`-A`) flag.
Backing up all databases is useful for creating a snapshot of the entire database server, whether for disaster recovery or migration purposes.
## Backup Modes
### Separate Backup Files (Default)
Using --all-databases without --all-in-one creates individual backup files for each database.
- Creates separate backup files for each database.
- Provides more flexibility in restoring individual databases or tables.
- Can be more manageable in cases where different databases have different retention policies.
- Might take slightly longer due to multiple file operations.
- It is the default behavior when using the `--all-databases` flag.
- It does not backup system databases (`information_schema`, `performance_schema`, `mysql`, `sys`, `innodb`,...).
**Command:**
```bash
docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \
-e "DB_PORT=3306" \
-e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \
jkaninda/mysql-bkup backup --all-databases
```
### Single Backup File
Using --all-in-one (-A) creates a single backup file containing all databases.
- Creates a single backup file containing all databases.
- Easier to manage if you need to restore everything at once.
- Faster to back up and restore in bulk.
- Can be problematic if you only need to restore a specific database or table.
- It is recommended to use this option for disaster recovery purposes.
- It backups system databases as well.
```bash
docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \
-e "DB_PORT=3306" \
-e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \
jkaninda/mysql-bkup backup --all-in-one
```
### When to Use Which?
- Use `--all-in-one` if you want a quick, simple backup for disaster recovery where you'll restore everything at once.
- Use `--all-databases` if you need granularity in restoring specific databases or tables without affecting others.

View File

@@ -19,7 +19,7 @@ The configuration file can be mounted into the container at `/config/config.yaml
### Key Features: ### Key Features:
- **Global Environment Variables**: Use these for databases that share the same configuration. - **Global Environment Variables**: Use these for databases that share the same configuration.
- **Database-Specific Overrides**: Override global settings for individual databases by specifying them in the configuration file or using the database name as a suffix in the variable name (e.g., `DB_HOST_DATABASE1`). - **Database-Specific Overrides**: Override global settings for individual databases by specifying them in the configuration file or using the database name as a prefix or suffix in the variable name (e.g., `DB_HOST_DATABASENAME` or `DATABASENAME_DB_HOST`).
- **Global Cron Expression**: Define a global `cronExpression` in the configuration file to schedule backups for all databases. If omitted, backups will run immediately. - **Global Cron Expression**: Define a global `cronExpression` in the configuration file to schedule backups for all databases. If omitted, backups will run immediately.
- **Configuration File Path**: Specify the configuration file path using: - **Configuration File Path**: Specify the configuration file path using:
- The `BACKUP_CONFIG_FILE` environment variable. - The `BACKUP_CONFIG_FILE` environment variable.
@@ -34,8 +34,8 @@ Below is an example configuration file (`config.yaml`) that defines multiple dat
```yaml ```yaml
# Optional: Define a global cron expression for scheduled backups. # Optional: Define a global cron expression for scheduled backups.
# Example: "@every 20m" (runs every 20 minutes). If omitted, backups run immediately. # Example: "@every 20m" (runs every 20 minutes). If omitted, backups run immediately.
cronExpression: "" cronExpression: "" # Optional: Define a global cron expression for scheduled backups.
backupRescueMode: false # Optional: Set to true to enable rescue mode for backups.
databases: databases:
- host: mysql1 # Optional: Overrides DB_HOST or uses DB_HOST_DATABASE1. - host: mysql1 # Optional: Overrides DB_HOST or uses DB_HOST_DATABASE1.
port: 3306 # Optional: Default is 5432. Overrides DB_PORT or uses DB_PORT_DATABASE1. port: 3306 # Optional: Default is 5432. Overrides DB_PORT or uses DB_PORT_DATABASE1.
@@ -89,7 +89,7 @@ services:
environment: environment:
## Specify the path to the configuration file ## Specify the path to the configuration file
- BACKUP_CONFIG_FILE=/backup/config.yaml - BACKUP_CONFIG_FILE=/backup/config.yaml
# Ensure the pg-bkup container is connected to the same network as your database # Ensure the mysql-bkup container is connected to the same network as your database
networks: networks:
- web - web

View File

@@ -2,7 +2,7 @@
title: Receive notifications title: Receive notifications
layout: default layout: default
parent: How Tos parent: How Tos
nav_order: 12 nav_order: 13
--- ---
# Receive Notifications # Receive Notifications

View File

@@ -10,6 +10,8 @@ nav_order: 1
**MYSQL-BKUP** is a Docker container image designed to **backup, restore, and migrate MySQL databases**. **MYSQL-BKUP** is a Docker container image designed to **backup, restore, and migrate MySQL databases**.
It supports a variety of storage options and ensures data security through GPG encryption. It supports a variety of storage options and ensures data security through GPG encryption.
**MYSQL-BKUP** is designed for seamless deployment on **Docker** and **Kubernetes**, simplifying MySQL backup, restoration, and migration across environments.
It is a lightweight, multi-architecture solution compatible with **Docker**, **Docker Swarm**, **Kubernetes**, and other container orchestration platforms.
--- ---
## Key Features ## Key Features
@@ -39,11 +41,21 @@ It supports a variety of storage options and ensures data security through GPG e
--- ---
## Use Cases ## 💡Use Cases
- **Automated Recurring Backups:** Schedule regular backups for MySQL databases. - **Scheduled Backups**: Automate recurring backups using Docker or Kubernetes.
- **Cross-Environment Migration:** Easily migrate MySQL databases across different environments using supported storage options. - **Disaster Recovery:** Quickly restore backups to a clean MySQL instance.
- **Secure Backup Management:** Protect your data with GPG encryption. - **Database Migration**: Seamlessly move data across environments using the built-in `migrate` feature.
- **Secure Archiving:** Keep backups encrypted and safely stored in the cloud or remote servers.
## ✅ Verified Platforms:
MYSQL-BKUP has been tested and runs successfully on:
- Docker
- Docker Swarm
- Kubernetes
- OpenShift
--- ---

View File

@@ -10,11 +10,11 @@ This guide provides quick examples for running backups using Docker CLI, Docker
--- ---
## Simple Backup Using Docker CLI ### Simple Backup Using Docker CLI
To run a one-time backup, bind your local volume to `/backup` in the container and execute the `backup` command: To perform a one-time backup, bind your local volume to `/backup` in the container and run the `backup` command:
```bash ```shell
docker run --rm --network your_network_name \ docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \ -v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \ -e "DB_HOST=dbhost" \
@@ -24,41 +24,58 @@ docker run --rm --network your_network_name \
jkaninda/mysql-bkup backup -d database_name jkaninda/mysql-bkup backup -d database_name
``` ```
### Using an Environment File Alternatively, use an environment file (`--env-file`) for configuration:
Alternatively, you can use an `--env-file` to pass a full configuration: ```shell
```bash
docker run --rm --network your_network_name \ docker run --rm --network your_network_name \
--env-file your-env-file \ --env-file your-env-file \
-v $PWD/backup:/backup/ \ -v $PWD/backup:/backup/ \
jkaninda/mysql-bkup backup -d database_name jkaninda/mysql-bkup backup -d database_name
``` ```
### Simple restore using Docker CLI ### Backup All Databases
To restore a database, bind your local volume to `/backup` in the container and run the `restore` command: To back up all databases on the server, use the `--all-databases` or `-a` flag. By default, this creates individual backup files for each database.
```shell ```shell
docker run --rm --network your_network_name \ docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \ -v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \ -e "DB_HOST=dbhost" \
-e "DB_PORT=3306" \ -e "DB_PORT=3306" \
-e "DB_USERNAME=username" \ -e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \ -e "DB_PASSWORD=password" \
jkaninda/mysql-bkup restore -d database_name -f backup_file.sql.gz jkaninda/mysql-bkup backup --all-databases --disable-compression
``` ```
> **Note:** Use the `--all-in-one` or `-A` flag to combine backups into a single file.
--- ---
## Simple Backup Using Docker Compose ### Simple Restore Using Docker CLI
Below is an example `docker-compose.yml` configuration for running a backup: To restore a database, bind your local volume to `/backup` and run the `restore` command:
```shell
docker run --rm --network your_network_name \
-v $PWD/backup:/backup/ \
-e "DB_HOST=dbhost" \
-e "DB_PORT=3306" \
-e "DB_USERNAME=username" \
-e "DB_PASSWORD=password" \
jkaninda/mysql-bkup restore -d database_name -f backup_file.sql.gz
```
---
### Backup with Docker Compose
Below is an example of a `docker-compose.yml` file for running a one-time backup:
```yaml ```yaml
services: services:
mysql-bkup: pg-bkup:
# In production, lock the image tag to a specific release version. # In production, pin your image tag to a specific release version instead of `latest`.
# Check https://github.com/jkaninda/mysql-bkup/releases for available releases. # See available releases: https://github.com/jkaninda/mysql-bkup/releases
image: jkaninda/mysql-bkup image: jkaninda/mysql-bkup
container_name: mysql-bkup container_name: mysql-bkup
command: backup command: backup
@@ -71,7 +88,6 @@ services:
- DB_USERNAME=bar - DB_USERNAME=bar
- DB_PASSWORD=password - DB_PASSWORD=password
- TZ=Europe/Paris - TZ=Europe/Paris
# Ensure the mysql-bkup container is connected to the same network as your database.
networks: networks:
- web - web
@@ -81,11 +97,11 @@ networks:
--- ---
## Recurring Backup with Docker ### Recurring Backups with Docker
To schedule recurring backups, use the `--cron-expression` flag: You can schedule recurring backups using the `--cron-expression` or `-e` flag:
```bash ```shell
docker run --rm --network network_name \ docker run --rm --network network_name \
-v $PWD/backup:/backup/ \ -v $PWD/backup:/backup/ \
-e "DB_HOST=hostname" \ -e "DB_HOST=hostname" \
@@ -98,9 +114,13 @@ For predefined schedules, refer to the [documentation](https://jkaninda.github.i
--- ---
## Backup Using Kubernetes ## Deploy on Kubernetes
Below is an example Kubernetes `Job` configuration for running a backup: For Kubernetes, you can deploy `mysql-bkup` as a Job or CronJob. Below are examples for both.
### Kubernetes Backup Job
This example defines a one-time backup job:
```yaml ```yaml
apiVersion: batch/v1 apiVersion: batch/v1
@@ -113,8 +133,8 @@ spec:
spec: spec:
containers: containers:
- name: mysql-bkup - name: mysql-bkup
# In production, lock the image tag to a specific release version. # Pin the image tag to a specific release version in production.
# Check https://github.com/jkaninda/mysql-bkup/releases for available releases. # See available releases: https://github.com/jkaninda/mysql-bkup/releases
image: jkaninda/mysql-bkup image: jkaninda/mysql-bkup
command: command:
- /bin/sh - /bin/sh
@@ -128,7 +148,7 @@ spec:
- name: DB_HOST - name: DB_HOST
value: "mysql" value: "mysql"
- name: DB_USERNAME - name: DB_USERNAME
value: "postgres" value: "user"
- name: DB_PASSWORD - name: DB_PASSWORD
value: "password" value: "password"
volumeMounts: volumeMounts:
@@ -137,11 +157,51 @@ spec:
volumes: volumes:
- name: backup - name: backup
hostPath: hostPath:
path: /home/toto/backup # Directory location on the host path: /home/toto/backup # Directory location on the host
type: Directory # Optional field type: Directory # Optional field
restartPolicy: Never restartPolicy: Never
``` ```
### Kubernetes CronJob for Scheduled Backups
For scheduled backups, use a `CronJob`:
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: pg-bkup-cronjob
spec:
schedule: "0 2 * * *" # Runs daily at 2 AM
jobTemplate:
spec:
template:
spec:
containers:
- name: pg-bkup
image: jkaninda/mysql-bkup
command:
- /bin/sh
- -c
- backup -d dbname
env:
- name: DB_HOST
value: "mysql"
- name: DB_USERNAME
value: "user"
- name: DB_PASSWORD
value: "password"
volumeMounts:
- mountPath: /backup
name: backup
volumes:
- name: backup
hostPath:
path: /home/toto/backup
type: Directory
restartPolicy: OnFailure
```
--- ---
## Key Notes ## Key Notes

View File

@@ -6,28 +6,31 @@ nav_order: 3
# Configuration Reference # Configuration Reference
Backup, restore, and migration targets, schedules, and retention policies are configured using **environment variables** or **CLI flags**. MySQL backup, restore, and migration processes can be configured using **environment variables** or **CLI flags**.
---
## CLI Utility Usage ## CLI Utility Usage
| Option | Short Flag | Description | The `mysql-bkup` CLI provides commands and options to manage MySQL backups efficiently.
|-------------------------|------------|-------------------------------------------------------------------------------|
| `pg-bkup` | `bkup` | CLI utility for managing PostgreSQL backups. | | Option | Short Flag | Description |
| `backup` | | Perform a backup operation. | |-------------------------|------------|-----------------------------------------------------------------------------------------|
| `restore` | | Perform a restore operation. | | `mysql-bkup` | `bkup` | CLI tool for managing MySQL backups, restoration, and migration. |
| `migrate` | | Migrate a database from one instance to another. | | `backup` | | Executes a backup operation. |
| `--storage` | `-s` | Storage type (`local`, `s3`, `ssh`, etc.). Default: `local`. | | `restore` | | Restores a database from a backup file. |
| `--file` | `-f` | File name for restoration. | | `migrate` | | Migrates a database from one instance to another. |
| `--path` | | Path for storage (e.g., `/custom_path` for S3 or `/home/foo/backup` for SSH). | | `--storage` | `-s` | Specifies the storage type (`local`, `s3`, `ssh`, etc.). Default: `local`. |
| `--config` | `-c` | Configuration file for multi database backup. (e.g: `/backup/config.yaml`). | | `--file` | `-f` | Defines the backup file name for restoration. |
| `--dbname` | `-d` | Database name. | | `--path` | | Sets the storage path (e.g., `/custom_path` for S3 or `/home/foo/backup` for SSH). |
| `--port` | `-p` | Database port. Default: `3306`. | | `--config` | `-c` | Provides a configuration file for multi-database backups (e.g., `/backup/config.yaml`). |
| `--disable-compression` | | Disable compression for database backups. | | `--dbname` | `-d` | Specifies the database name to back up or restore. |
| `--cron-expression` | `-e` | Cron expression for scheduled backups (e.g., `0 0 * * *` or `@daily`). | | `--port` | `-p` | Defines the database port. Default: `3306`. |
| `--help` | `-h` | Display help message and exit. | | `--disable-compression` | | Disables compression for database backups. |
| `--version` | `-V` | Display version information and exit. | | `--cron-expression` | `-e` | Schedules backups using a cron expression (e.g., `0 0 * * *` or `@daily`). |
| `--all-databases` | `-a` | Backs up all databases separately (e.g., `backup --all-databases`). |
| `--all-in-one` | `-A` | Backs up all databases in a single file (e.g., `backup --all-databases --single-file`). |
| `--custom-name` | `` | Sets custom backup name for one time backup |
| `--help` | `-h` | Displays the help message and exits. |
| `--version` | `-V` | Shows version information and exits. |
--- ---
@@ -40,6 +43,8 @@ Backup, restore, and migration targets, schedules, and retention policies are co
| `DB_NAME` | Optional (if provided via `-d` flag) | Database name. | | `DB_NAME` | Optional (if provided via `-d` flag) | Database name. |
| `DB_USERNAME` | Required | Database username. | | `DB_USERNAME` | Required | Database username. |
| `DB_PASSWORD` | Required | Database password. | | `DB_PASSWORD` | Required | Database password. |
| `DB_SSL_CA` | Optional | Database client CA certificate file |
| `DB_SSL_MODE` | Optional(`0 or 1`) default: `0` | Database client Enable CA validation |
| `AWS_ACCESS_KEY` | Required for S3 storage | AWS S3 Access Key. | | `AWS_ACCESS_KEY` | Required for S3 storage | AWS S3 Access Key. |
| `AWS_SECRET_KEY` | Required for S3 storage | AWS S3 Secret Key. | | `AWS_SECRET_KEY` | Required for S3 storage | AWS S3 Secret Key. |
| `AWS_BUCKET_NAME` | Required for S3 storage | AWS S3 Bucket Name. | | `AWS_BUCKET_NAME` | Required for S3 storage | AWS S3 Bucket Name. |

8
go.mod
View File

@@ -1,16 +1,16 @@
module github.com/jkaninda/mysql-bkup module github.com/jkaninda/mysql-bkup
go 1.23.2 go 1.24.5
require github.com/spf13/pflag v1.0.6 // indirect require github.com/spf13/pflag v1.0.9 // indirect
require ( require (
github.com/go-mail/mail v2.3.1+incompatible github.com/go-mail/mail v2.3.1+incompatible
github.com/jkaninda/encryptor v0.0.0-20241111100652-926393c9437e github.com/jkaninda/encryptor v0.0.0-20241111100652-926393c9437e
github.com/jkaninda/go-storage v0.1.3 github.com/jkaninda/go-storage v0.1.3
github.com/jkaninda/go-utils v0.1.1 github.com/jkaninda/go-utils v0.1.3
github.com/robfig/cron/v3 v3.0.1 github.com/robfig/cron/v3 v3.0.1
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.10.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

12
go.sum
View File

@@ -43,8 +43,8 @@ github.com/jkaninda/encryptor v0.0.0-20241111100652-926393c9437e h1:jtFKZHt/PLGQ
github.com/jkaninda/encryptor v0.0.0-20241111100652-926393c9437e/go.mod h1:Y1EXpPWQ9PNd7y7E6ez3xgnzZc8fuDWXwX/1/dXNCE4= github.com/jkaninda/encryptor v0.0.0-20241111100652-926393c9437e/go.mod h1:Y1EXpPWQ9PNd7y7E6ez3xgnzZc8fuDWXwX/1/dXNCE4=
github.com/jkaninda/go-storage v0.1.3 h1:lEpHVgFLKSvjsi/6tAek96Y07za3vxmsXF2/+jiCMZU= github.com/jkaninda/go-storage v0.1.3 h1:lEpHVgFLKSvjsi/6tAek96Y07za3vxmsXF2/+jiCMZU=
github.com/jkaninda/go-storage v0.1.3/go.mod h1:zVRnLprBk/9AUz2+za6Y03MgoNYrqKLy3edVtjqMaps= github.com/jkaninda/go-storage v0.1.3/go.mod h1:zVRnLprBk/9AUz2+za6Y03MgoNYrqKLy3edVtjqMaps=
github.com/jkaninda/go-utils v0.1.1 h1:PMrtXR9d51YzHo85y9Z6YVL0YyBURbRTPemHVbFDqZg= github.com/jkaninda/go-utils v0.1.3 h1:BhVRHpR6JIGhE7JVgqOgiFketIqmNtvMvYpveVwDqqY=
github.com/jkaninda/go-utils v0.1.1/go.mod h1:pf0/U6k4JbxlablM2G4eSTZdQ2LFshfAsCK5Q8qNfGo= github.com/jkaninda/go-utils v0.1.3/go.mod h1:Aa54jEAcDykc3CnOdreqZG80UfSZOvrYecyusu+oPb4=
github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg= github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg=
github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI= github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -68,10 +68,10 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=

35
migrations/init.sql Normal file
View File

@@ -0,0 +1,35 @@
-- Create the database testdb2 and testdb3
CREATE DATABASE IF NOT EXISTS testdb2;
CREATE DATABASE IF NOT EXISTS testdb3;
CREATE DATABASE IF NOT EXISTS fakedb;
USE testdb;
-- Create the 'users' table
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Create the 'orders' table
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status ENUM('pending', 'completed', 'canceled') NOT NULL DEFAULT 'pending',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- Insert fake users
INSERT INTO users (name, email) VALUES
('Alice Smith', 'alice@example.com'),
('Bob Johnson', 'bob@example.com'),
('Charlie Brown', 'charlie@example.com');
-- Insert fake orders
INSERT INTO orders (user_id, amount, status) VALUES
(1, 100.50, 'completed'),
(2, 200.75, 'pending'),
(3, 50.00, 'canceled');

View File

@@ -0,0 +1,13 @@
#cronExpression: "@every 20s"
#backupRescueMode: false
databases:
- host: 127.0.0.1
port: 3306
name: testdb
user: user
password: password
- name: testdb2
# database credentials from environment variables
#TESTDB2_DB_USERNAME
#TESTDB2_DB_PASSWORD
#TESTDB2_DB_HOST

View File

@@ -39,7 +39,7 @@ func azureBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to Azure Blob Storage") utils.Info("Backup database to Azure Blob Storage")
// Backup database // Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile) err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil { if err != nil {
recoverMode(err, "Error backing up database") recoverMode(err, "Error backing up database")
return return

View File

@@ -27,6 +27,7 @@ package pkg
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
"github.com/jkaninda/encryptor" "github.com/jkaninda/encryptor"
"github.com/jkaninda/go-storage/pkg/local" "github.com/jkaninda/go-storage/pkg/local"
@@ -50,6 +51,7 @@ func StartBackup(cmd *cobra.Command) {
if err != nil { if err != nil {
dbConf = initDbConfig(cmd) dbConf = initDbConfig(cmd)
if config.cronExpression == "" { if config.cronExpression == "" {
config.allowCustomName = true
createBackupTask(dbConf, config) createBackupTask(dbConf, config)
} else { } else {
if utils.IsValidCronExpression(config.cronExpression) { if utils.IsValidCronExpression(config.cronExpression) {
@@ -112,9 +114,12 @@ func multiBackupTask(databases []Database, bkConfig *BackupConfig) {
// createBackupTask backup task // createBackupTask backup task
func createBackupTask(db *dbConfig, config *BackupConfig) { func createBackupTask(db *dbConfig, config *BackupConfig) {
if config.all && !config.singleFile { if config.all && !config.allInOne {
backupAll(db, config) backupAll(db, config)
} else { } else {
if db.dbName == "" && !config.all {
utils.Fatal("Database name is required, use DB_NAME environment variable or -d flag")
}
backupTask(db, config) backupTask(db, config)
} }
} }
@@ -141,14 +146,21 @@ func backupTask(db *dbConfig, config *BackupConfig) {
utils.Info("Starting backup task...") utils.Info("Starting backup task...")
startTime = time.Now() startTime = time.Now()
prefix := db.dbName prefix := db.dbName
if config.all && config.singleFile { if config.all && config.allInOne {
prefix = "all_databases" prefix = "all_databases"
} }
// Generate file name // Generate file name
backupFileName := fmt.Sprintf("%s_%s.sql.gz", prefix, time.Now().Format("20060102_150405")) backupFileName := fmt.Sprintf("%s_%s.sql.gz", prefix, time.Now().Format("20060102_150405"))
if config.disableCompression { if config.disableCompression {
backupFileName = fmt.Sprintf("%s_%s.sql", prefix, time.Now().Format("20060102_150405")) backupFileName = fmt.Sprintf("%s_%s.sql", prefix, time.Now().Format("20060102_150405"))
} }
if config.customName != "" && config.allowCustomName && !config.all {
backupFileName = fmt.Sprintf("%s.sql.gz", config.customName)
if config.disableCompression {
backupFileName = fmt.Sprintf("%s.sql", config.customName)
}
}
config.backupFileName = backupFileName config.backupFileName = backupFileName
s := strings.ToLower(config.storage) s := strings.ToLower(config.storage)
switch s { switch s {
@@ -240,8 +252,10 @@ func BackupDatabase(db *dbConfig, backupFileName string, disableCompression, all
dumpArgs := []string{fmt.Sprintf("--defaults-file=%s", mysqlClientConfig)} dumpArgs := []string{fmt.Sprintf("--defaults-file=%s", mysqlClientConfig)}
if all && singleFile { if all && singleFile {
utils.Info("Backing up all databases...")
dumpArgs = append(dumpArgs, "--all-databases", "--single-transaction", "--routines", "--triggers") dumpArgs = append(dumpArgs, "--all-databases", "--single-transaction", "--routines", "--triggers")
} else { } else {
utils.Info("Backing up %s database...", db.dbName)
dumpArgs = append(dumpArgs, db.dbName) dumpArgs = append(dumpArgs, db.dbName)
} }
@@ -302,7 +316,7 @@ func runCommandWithCompression(command string, args []string, outputPath string)
// localBackup backup database to local storage // localBackup backup database to local storage
func localBackup(db *dbConfig, config *BackupConfig) { func localBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to local storage") utils.Info("Backup database to local storage")
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile) err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil { if err != nil {
recoverMode(err, "Error backing up database") recoverMode(err, "Error backing up database")
return return
@@ -388,7 +402,7 @@ func listDatabases(db dbConfig) ([]string, error) {
databases := []string{} databases := []string{}
// Create the mysql client config file // Create the mysql client config file
if err := createMysqlClientConfigFile(db); err != nil { if err := createMysqlClientConfigFile(db); err != nil {
return databases, fmt.Errorf(err.Error()) return databases, errors.New(err.Error())
} }
utils.Info("Listing databases...") utils.Info("Listing databases...")
// Step 1: List all databases // Step 1: List all databases

View File

@@ -78,7 +78,9 @@ type BackupConfig struct {
storage string storage string
cronExpression string cronExpression string
all bool all bool
singleFile bool allInOne bool
customName string
allowCustomName bool
} }
type FTPConfig struct { type FTPConfig struct {
host string host string
@@ -258,8 +260,12 @@ func initBackupConfig(cmd *cobra.Command) *BackupConfig {
prune = true prune = true
} }
disableCompression, _ = cmd.Flags().GetBool("disable-compression") disableCompression, _ = cmd.Flags().GetBool("disable-compression")
all, _ := cmd.Flags().GetBool("all") customName, _ := cmd.Flags().GetString("custom-name")
singleFile, _ := cmd.Flags().GetBool("single-file") all, _ := cmd.Flags().GetBool("all-databases")
allInOne, _ := cmd.Flags().GetBool("all-in-one")
if allInOne {
all = true
}
_, _ = cmd.Flags().GetString("mode") _, _ = cmd.Flags().GetString("mode")
passphrase := os.Getenv("GPG_PASSPHRASE") passphrase := os.Getenv("GPG_PASSPHRASE")
_ = utils.GetEnv(cmd, "path", "AWS_S3_PATH") _ = utils.GetEnv(cmd, "path", "AWS_S3_PATH")
@@ -286,7 +292,8 @@ func initBackupConfig(cmd *cobra.Command) *BackupConfig {
config.usingKey = usingKey config.usingKey = usingKey
config.cronExpression = cronExpression config.cronExpression = cronExpression
config.all = all config.all = all
config.singleFile = singleFile config.allInOne = allInOne
config.customName = customName
return &config return &config
} }

View File

@@ -26,6 +26,7 @@ package pkg
import ( import (
"bytes" "bytes"
"errors"
"fmt" "fmt"
goutils "github.com/jkaninda/go-utils" goutils "github.com/jkaninda/go-utils"
"github.com/jkaninda/mysql-bkup/utils" "github.com/jkaninda/mysql-bkup/utils"
@@ -37,7 +38,7 @@ import (
) )
func intro() { func intro() {
fmt.Println("Starting MySQL Backup...") fmt.Println("Starting MYSQL-BKUP...")
fmt.Printf("Version: %s\n", utils.Version) fmt.Printf("Version: %s\n", utils.Version)
fmt.Println("Copyright (c) 2024 Jonas Kaninda") fmt.Println("Copyright (c) 2024 Jonas Kaninda")
} }
@@ -70,14 +71,13 @@ func deleteTemp() {
func testDatabaseConnection(db *dbConfig) error { func testDatabaseConnection(db *dbConfig) error {
// Create the mysql client config file // Create the mysql client config file
if err := createMysqlClientConfigFile(*db); err != nil { if err := createMysqlClientConfigFile(*db); err != nil {
return fmt.Errorf(err.Error()) return errors.New(err.Error())
} }
utils.Info("Connecting to %s database ...", db.dbName) utils.Info("Connecting to %s database ...", db.dbName)
// Set database name for notification error // Set database name for notification error
utils.DatabaseName = db.dbName utils.DatabaseName = db.dbName
// Prepare the command to test the database connection // Prepare the command to test the database connection
//cmd := exec.Command("mariadb", "-h", db.dbHost, "-P", db.dbPort, "-u", db.dbUserName, db.dbName, "-e", "quit")
cmd := exec.Command("mariadb", fmt.Sprintf("--defaults-file=%s", mysqlClientConfig), db.dbName, "-e", "quit") cmd := exec.Command("mariadb", fmt.Sprintf("--defaults-file=%s", mysqlClientConfig), db.dbName, "-e", "quit")
// Capture the output // Capture the output
var out bytes.Buffer var out bytes.Buffer

View File

@@ -39,7 +39,7 @@ import (
func sshBackup(db *dbConfig, config *BackupConfig) { func sshBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to Remote server") utils.Info("Backup database to Remote server")
// Backup database // Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile) err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil { if err != nil {
recoverMode(err, "Error backing up database") recoverMode(err, "Error backing up database")
return return
@@ -160,7 +160,7 @@ func ftpBackup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to the remote FTP server") utils.Info("Backup database to the remote FTP server")
// Backup database // Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile) err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil { if err != nil {
recoverMode(err, "Error backing up database") recoverMode(err, "Error backing up database")
return return

View File

@@ -57,11 +57,17 @@ func StartRestore(cmd *cobra.Command) {
} }
func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) { func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) {
utils.Info("Restore database from local") utils.Info("Restore database from local")
basePath := filepath.Dir(restoreConf.file)
fileName := filepath.Base(restoreConf.file)
restoreConf.file = fileName
if basePath == "" || basePath == "." {
basePath = storagePath
}
localStorage := local.NewStorage(local.Config{ localStorage := local.NewStorage(local.Config{
RemotePath: storagePath, RemotePath: basePath,
LocalPath: tmpPath, LocalPath: tmpPath,
}) })
err := localStorage.CopyFrom(restoreConf.file) err := localStorage.CopyFrom(fileName)
if err != nil { if err != nil {
utils.Fatal("Error copying backup file: %s", err) utils.Fatal("Error copying backup file: %s", err)
} }
@@ -69,87 +75,79 @@ func localRestore(dbConf *dbConfig, restoreConf *RestoreConfig) {
} }
// RestoreDatabase restore database // RestoreDatabase restores the database from a backup file
func RestoreDatabase(db *dbConfig, conf *RestoreConfig) { func RestoreDatabase(db *dbConfig, conf *RestoreConfig) {
if conf.file == "" { if conf.file == "" {
utils.Fatal("Error, file required") utils.Fatal("Error, file required")
} }
extension := filepath.Ext(filepath.Join(tmpPath, conf.file))
rFile, err := os.ReadFile(filepath.Join(tmpPath, conf.file)) filePath := filepath.Join(tmpPath, conf.file)
outputFile := RemoveLastExtension(filepath.Join(tmpPath, conf.file)) rFile, err := os.ReadFile(filePath)
if err != nil { if err != nil {
utils.Fatal("Error reading backup file: %s ", err) utils.Fatal("Error reading backup file: %v", err)
} }
extension := filepath.Ext(filePath)
outputFile := RemoveLastExtension(filePath)
if extension == ".gpg" { if extension == ".gpg" {
decryptBackup(conf, rFile, outputFile)
if conf.usingKey {
utils.Info("Decrypting backup using private key...")
utils.Warn("Backup decryption using a private key is not fully supported")
prKey, err := os.ReadFile(conf.privateKey)
if err != nil {
utils.Fatal("Error reading public key: %s ", err)
}
err = encryptor.DecryptWithPrivateKey(rFile, outputFile, prKey, conf.passphrase)
if err != nil {
utils.Fatal("error during decrypting backup %v", err)
}
utils.Info("Decrypting backup using private key...done")
} else {
if conf.passphrase == "" {
utils.Error("Error, passphrase or private key required")
utils.Fatal("Your file seems to be a GPG file.\nYou need to provide GPG keys. GPG_PASSPHRASE or GPG_PRIVATE_KEY environment variable is required.")
} else {
utils.Info("Decrypting backup using passphrase...")
// decryptWithGPG file
err := encryptor.Decrypt(rFile, outputFile, conf.passphrase)
if err != nil {
utils.Fatal("Error decrypting file %s %v", file, err)
}
utils.Info("Decrypting backup using passphrase...done")
// Update file name
conf.file = RemoveLastExtension(file)
}
}
} }
if utils.FileExists(filepath.Join(tmpPath, conf.file)) { restorationFile := filepath.Join(tmpPath, conf.file)
err = testDatabaseConnection(db) if !utils.FileExists(restorationFile) {
utils.Fatal("File not found: %s", restorationFile)
}
if err := testDatabaseConnection(db); err != nil {
utils.Fatal("Error connecting to the database: %v", err)
}
utils.Info("Restoring database...")
restoreDatabaseFile(db, restorationFile)
}
func decryptBackup(conf *RestoreConfig, rFile []byte, outputFile string) {
if conf.usingKey {
utils.Info("Decrypting backup using private key...")
prKey, err := os.ReadFile(conf.privateKey)
if err != nil { if err != nil {
utils.Fatal("Error connecting to the database %v", err) utils.Fatal("Error reading private key: %v", err)
} }
utils.Info("Restoring database...") if err := encryptor.DecryptWithPrivateKey(rFile, outputFile, prKey, conf.passphrase); err != nil {
utils.Fatal("Error decrypting backup: %v", err)
extension := filepath.Ext(filepath.Join(tmpPath, conf.file))
// Restore from a compressed file / .sql.gz
if extension == ".gz" {
str := "zcat " + filepath.Join(tmpPath, conf.file) + " | mariadb " + fmt.Sprintf("--defaults-file=%s", mysqlClientConfig) + " " + db.dbName
output, err := exec.Command("sh", "-c", str).Output()
if err != nil {
utils.Fatal("Error, in restoring the database %v output: %v", err, output)
}
utils.Info("Restoring database... done")
utils.Info("Database has been restored")
// Delete temp
deleteTemp()
} else if extension == ".sql" {
// Restore from SQL file
str := "cat " + filepath.Join(tmpPath, conf.file) + " | mariadb " + fmt.Sprintf("--defaults-file=%s", mysqlClientConfig) + " " + db.dbName
output, err := exec.Command("sh", "-c", str).Output()
if err != nil {
utils.Fatal("Error, in restoring the database %v output: %v", err, output)
}
utils.Info("Restoring database... done")
utils.Info("Database has been restored")
// Delete temp
deleteTemp()
} else {
utils.Fatal("Unknown file extension %s", extension)
} }
} else { } else {
utils.Fatal("File not found in %s", filepath.Join(tmpPath, conf.file)) if conf.passphrase == "" {
utils.Fatal("Passphrase or private key required for GPG file.")
}
utils.Info("Decrypting backup using passphrase...")
if err := encryptor.Decrypt(rFile, outputFile, conf.passphrase); err != nil {
utils.Fatal("Error decrypting file: %v", err)
}
conf.file = RemoveLastExtension(conf.file)
} }
} }
func restoreDatabaseFile(db *dbConfig, restorationFile string) {
extension := filepath.Ext(restorationFile)
var cmdStr string
switch extension {
case ".gz":
cmdStr = fmt.Sprintf("zcat %s | mariadb --defaults-file=%s %s", restorationFile, mysqlClientConfig, db.dbName)
case ".sql":
cmdStr = fmt.Sprintf("cat %s | mariadb --defaults-file=%s %s", restorationFile, mysqlClientConfig, db.dbName)
default:
utils.Fatal("Unknown file extension: %s", extension)
}
cmd := exec.Command("sh", "-c", cmdStr)
output, err := cmd.CombinedOutput()
if err != nil {
utils.Fatal("Error restoring database: %v\nOutput: %s", err, string(output))
}
utils.Info("Database has been restored successfully.")
deleteTemp()
}

View File

@@ -39,7 +39,7 @@ func s3Backup(db *dbConfig, config *BackupConfig) {
utils.Info("Backup database to s3 storage") utils.Info("Backup database to s3 storage")
// Backup database // Backup database
err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.singleFile) err := BackupDatabase(db, config.backupFileName, disableCompression, config.all, config.allInOne)
if err != nil { if err != nil {
recoverMode(err, "Error backing up database") recoverMode(err, "Error backing up database")
return return

View File

@@ -54,7 +54,6 @@ var dbHVars = []string{
"DB_HOST", "DB_HOST",
"DB_PASSWORD", "DB_PASSWORD",
"DB_USERNAME", "DB_USERNAME",
"DB_NAME",
} }
var tdbRVars = []string{ var tdbRVars = []string{
"TARGET_DB_HOST", "TARGET_DB_HOST",