diff --git a/.github/kubeconform.sh b/.github/kubeconform.sh new file mode 100755 index 0000000..0c8a92d --- /dev/null +++ b/.github/kubeconform.sh @@ -0,0 +1,22 @@ +#!/bin/bash +# +# use kubeconform to validate helm generated kubernetes manifest +# + +set -o errexit +set -o pipefail + +CHART_DIRS="$(git diff --find-renames --name-only "$(git rev-parse --abbrev-ref HEAD)" remotes/origin/main | grep '[cC]hart.yaml' | sed -e 's#/[Cc]hart.yaml##g')" + +# install kubeconform +curl --silent --show-error --fail --location --output /tmp/kubeconform.tar.gz https://github.com/yannh/kubeconform/releases/download/"${KUBECONFORM_VERSION}"/kubeconform-linux-amd64.tar.gz +sudo tar -C /usr/local/bin -xf /tmp/kubeconform.tar.gz kubeconform + +# validate charts +for CHART_DIR in ${CHART_DIRS};do + echo "helm dependency build..." + helm dependency build "${CHART_DIR}" + + echo "kubeconform(ing) ${CHART_DIR##charts/} chart..." + helm template "${CHART_DIR}" | kubeconform --strict --verbose --kubernetes-version "${KUBERNETES_VERSION#v}" +done diff --git a/.github/kubeval.sh b/.github/kubeval.sh deleted file mode 100755 index ce29534..0000000 --- a/.github/kubeval.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -# -# use kubeval to validate helm generated kubernetes manifest -# - -set -o errexit -set -o pipefail - -CHART_DIRS="$(git diff --find-renames --name-only "$(git rev-parse --abbrev-ref HEAD)" remotes/origin/main -- charts | grep '[cC]hart.yaml' | sed -e 's#/[Cc]hart.yaml##g')" -SCHEMA_LOCATION="https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/" - -# install kubeval -curl --silent --show-error --fail --location --output /tmp/kubeval.tar.gz https://github.com/instrumenta/kubeval/releases/download/"${KUBEVAL_VERSION}"/kubeval-linux-amd64.tar.gz -sudo tar -C /usr/local/bin -xf /tmp/kubeval.tar.gz kubeval - -# validate charts -for CHART_DIR in ${CHART_DIRS};do - echo "helm dependency build..." - helm dependency build "${CHART_DIR}" - - echo "kubeval(idating) ${CHART_DIR##charts/} chart..." - helm template "${CHART_DIR}" | kubeval --strict --kubernetes-version "${KUBERNETES_VERSION#v}" --schema-location "${SCHEMA_LOCATION}" -done diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d626516..a20e6b4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -6,26 +6,26 @@ on: - 'charts/**/**' env: - helm-version: "v3.10.2" - kubeval-version: "v0.16.1" + helm-version: "v3.12.2" + kubeconform-version: "v0.6.3" jobs: lint-chart: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Helm uses: azure/setup-helm@v3.5 with: version: "${{ env.helm-version }}" - - uses: actions/setup-python@v4.6.0 + - uses: actions/setup-python@v4.7.1 with: python-version: 3.7 - name: Set up chart-testing - uses: helm/chart-testing-action@v2.4.0 + uses: helm/chart-testing-action@v2.6.1 - name: Run chart-testing (lint) run: ct lint --config .github/ct.yaml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Lint Code Base @@ -43,68 +43,65 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} VALIDATE_ALL_CODEBASE: false VALIDATE_JSCPD: false - VALIDATE_KUBERNETES_KUBEVAL: false + VALIDATE_KUBERNETES_KUBECONFORM: false VALIDATE_YAML: false - kubeval-chart: + kubeconform-chart: runs-on: ubuntu-22.04 needs: - lint-chart strategy: matrix: k8s: - - v1.23.13 - - v1.24.7 - - v1.25.3 + - v1.25.11 + - v1.26.6 + - v1.27.3 + steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Helm uses: azure/setup-helm@v3.5 with: version: "${{ env.helm-version }}" - - name: Run kubeval + - name: Run kubeconform env: KUBERNETES_VERSION: ${{ matrix.k8s }} - KUBEVAL_VERSION: "${{ env.kubeval-version }}" - run: .github/kubeval.sh + KUBECONFORM_VERSION: "${{ env.kubeconform-version }}" + run: .github/kubeconform.sh install-chart: name: install-chart runs-on: ubuntu-20.04 needs: - - kubeval-chart + - kubeconform-chart strategy: matrix: k8s: - - v1.23.13 - - v1.24.7 - - v1.25.3 + - v1.25.11 + - v1.26.6 + - v1.27.3 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Helm uses: azure/setup-helm@v3.5 with: version: "${{ env.helm-version }}" - - uses: actions/setup-python@v4.6.0 + - uses: actions/setup-python@v4.7.1 with: python-version: 3.7 - name: Set up chart-testing - uses: helm/chart-testing-action@v2.4.0 + uses: helm/chart-testing-action@v2.6.1 - name: Run chart-testing (list-changed) id: list-changed - run: | - changed=$(ct list-changed --config .github/ct.yaml) - if [[ -n "$changed" ]]; then - echo "::set-output name=changed::true" - fi + run: if [[ -n "$(ct list-changed --config .github/ct.yaml)" ]]; then echo 'changed=true' >> "$GITHUB_OUTPUT"; fi - name: Create kind cluster - uses: helm/kind-action@v1.5.0 + uses: helm/kind-action@v1.8.0 if: steps.list-changed.outputs.changed == 'true' with: config: .github/kind-config.yaml diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 5c95b09..6b106bd 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -28,6 +28,6 @@ jobs: version: "${{ env.helm-version }}" - name: Run chart-releaser - uses: helm/chart-releaser-action@v1.5.0 + uses: helm/chart-releaser-action@v1.6.0 env: CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/sync-readme.yaml b/.github/workflows/sync-readme.yaml index 6dc77d1..938be04 100644 --- a/.github/workflows/sync-readme.yaml +++ b/.github/workflows/sync-readme.yaml @@ -11,12 +11,12 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: | cp -f README.md ${{ runner.temp }}/README.md - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: gh-pages diff --git a/charts/fluentd-elasticsearch/Chart.yaml b/charts/fluentd-elasticsearch/Chart.yaml index 798844c..3c62b65 100644 --- a/charts/fluentd-elasticsearch/Chart.yaml +++ b/charts/fluentd-elasticsearch/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: fluentd-elasticsearch -version: 13.9.0 -appVersion: v4.2.3 +version: 13.11.0 +appVersion: v4.4.4 type: application home: https://www.fluentd.org/ description: A Fluentd Helm chart for Kubernetes with Elasticsearch output diff --git a/charts/mysqldump/Chart.yaml b/charts/mysqldump/Chart.yaml index ce4f24e..57aeef2 100644 --- a/charts/mysqldump/Chart.yaml +++ b/charts/mysqldump/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 -version: 6.0.0 -appVersion: v3.0.16 +version: 6.3.0 +appVersion: v3.1.1 description: A Helm chart to help backup MySQL databases using mysqldump name: mysqldump keywords: @@ -18,6 +18,6 @@ maintainers: type: application dependencies: - name: mariadb - version: 11.0.14 + version: 12.2.9 repository: "https://charts.bitnami.com/bitnami" condition: mariadb.enabled diff --git a/charts/mysqldump/README.md b/charts/mysqldump/README.md index 212abcc..df653ae 100644 --- a/charts/mysqldump/README.md +++ b/charts/mysqldump/README.md @@ -50,7 +50,7 @@ The following tables lists the configurable parameters of the mysqldump chart an | image.repository | Name of image to use | monotek/gcloud-mysql | | image.tag | Version of image to use (uses appVersion form Chart.yaml as default if not set) | "" | | image.pullPolicy | Pull Policy to use for image | IfNotPresent | -| mysql.db | single mysql db to backup (optional) | mysql | +| mysql.db | mysql db(s) to backup (optional) | mysql | | mysql.host | mysql host to backup | mysql | | mysql.username | mysql username | root | | mysql.password | mysql password | "" | @@ -59,6 +59,7 @@ The following tables lists the configurable parameters of the mysqldump chart an | mysql.port | mysql port | 3306 | | schedule | crontab schedule to run on. set as `now` to run as a one time job | "0 3 \* \* \*" | | options | options to pass onto MySQL | "--opt --single-transaction" | +| rsync.options | options to pass onto rsync | "-av" | | debug | print some extra debug logs during backup | false | | dumpAllToStdout | dump all database contents to stdout when not uploading | false | | additionalSteps | run these extra shell steps after all backup jobs completed | [] | @@ -72,7 +73,7 @@ The following tables lists the configurable parameters of the mysqldump chart an | persistence.storageClass | storage class to use for PVC | | | persistence.subPath | subPath for PVC | | | allDatabases.enabled | backup all databases | true | -| allDatabases.SingleSqlFile | backup all databases to single file | false | +| allDatabases.SingleSqlFile | backup all databases to single file (works with mysql.db too) | false | | housekeeping.enabled | delete olf backups in pvc | true | | housekeeping.keepDays | keep last x days of backups in PVC | 10 | | saveToDirectory | saves the sql backup to a directory named like the database or alldatabases | false | @@ -99,6 +100,14 @@ The following tables lists the configurable parameters of the mysqldump chart an | upload.openstack.existingSecret | optional, specify a secret name to use for password | | | upload.openstack.existingSecretKey | optional, specify a secret key to use for password | openstack-backup-password | | upload.openstack.ttlDays | days to set time-to-live on uploaded objects (0 to disable) | 30 | +| upload.s3.enabled | upload backups to s3 storage | false | +| upload.s3.bucketname | s3 bucket name | mysql-backup | +| upload.s3.endpoint | URL endpoint of the S3 service | | +| upload.s3.region | AWS region to use | us-east-1 | +| upload.s3.accesskey | s3 access key | "" | +| upload.s3.secretkey | s3 secret key | "" | +| upload.s3.existingSecret | optional, existing secret name, used to get s3 Secret key (if set) | "" | +| upload.s3.existingSecretKey | optional, specify a secret key to use for s3 Secret key | S3_SECRET_KEY | | resources | resource definitions | {} | | nodeSelector | k8s-node selector | {} | | tolerations | tolerations | \[] | diff --git a/charts/mysqldump/files/job.tpl b/charts/mysqldump/files/job.tpl index 64562b4..18e33de 100755 --- a/charts/mysqldump/files/job.tpl +++ b/charts/mysqldump/files/job.tpl @@ -18,7 +18,7 @@ spec: image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy | quote }} command: ["/bin/bash", "/scripts/backup.sh"] -{{- if or .Values.mysql.existingSecret .Values.upload.openstack.existingSecret }} +{{- if or .Values.mysql.existingSecret .Values.upload.openstack.existingSecret .Values.upload.s3.existingSecret }} env: {{- end }} {{- if .Values.mysql.existingSecret }} @@ -42,6 +42,17 @@ spec: {{- else }} key: "openstack-backup-password" {{- end }} +{{- end }} +{{- if .Values.upload.s3.existingSecret }} + - name: S3_SECRET_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.upload.s3.existingSecret | quote }} + {{- if .Values.upload.s3.existingSecretKey }} + key: {{ .Values.upload.s3.existingSecretKey | quote }} + {{- else }} + key: "secret-key" + {{- end }} {{- end }} envFrom: - configMapRef: @@ -49,6 +60,10 @@ spec: {{- if not .Values.mysql.existingSecret }} - secretRef: name: "{{ template "mysqldump.fullname" . }}" +{{- end }} +{{- if and (.Values.upload.s3.enabled) (not .Values.upload.s3.existingSecret) }} + - secretRef: + name: "{{ template "mysqldump.fullname" . }}-s3-secretkey" {{- end }} volumeMounts: - name: backups diff --git a/charts/mysqldump/templates/configmap-script.yaml b/charts/mysqldump/templates/configmap-script.yaml index 5513b7a..4c03de0 100644 --- a/charts/mysqldump/templates/configmap-script.yaml +++ b/charts/mysqldump/templates/configmap-script.yaml @@ -4,7 +4,7 @@ kind: ConfigMap metadata: name: {{ template "mysqldump.fullname" . }}-script labels: -{{ include "mysqldump.labels" . | indent 4 }} + {{- include "mysqldump.labels" . | nindent 4 }} data: {{- if .Values.upload.openstack.enabled }} openstack-upload.py: |- @@ -35,36 +35,41 @@ data: {{ if or (.Values.persistence.enabled) (.Values.persistentVolumeClaim) }} {{ if .Values.housekeeping.enabled }} echo "delete old backups" - find ${BACKUP_DIR} -maxdepth 2 -mtime +${KEEP_DAYS} -regex "^${BACKUP_DIR}/.*[0-9]*_.*\.sql\.gz$" -type f -exec rm {} \; + find ${BACKUP_DIR} -type d -name 'lost+found' -prune -o -maxdepth 2 -mtime +${KEEP_DAYS} -regex "^${BACKUP_DIR}/.*[0-9]*_.*\.sql\.gz$" -type f -exec rm {} \; {{ end -}} {{ end -}} - {{ if and (.Values.mysql.db) (eq .Values.allDatabases.enabled false) }} - MYSQL_DB="{{ .Values.mysql.db }}" - echo "Backing up single db ${MYSQL_DB}" - {{ if .Values.saveToDirectory }}mkdir -p "${BACKUP_DIR}"/"${MYSQL_DB}"{{ end }} - mysqldump ${MYSQL_OPTS} -h ${MYSQL_HOST} -P ${MYSQL_PORT} -u ${MYSQL_USERNAME} --databases ${MYSQL_DB} | gzip > ${BACKUP_DIR}/{{ if .Values.saveToDirectory }}${MYSQL_DB}/{{ end }}${TIMESTAMP}_${MYSQL_DB}.sql.gz - rc=$? - {{ else if and (.Values.allDatabases.enabled) (eq .Values.allDatabases.singleBackupFile false)}} - for MYSQL_DB in $(mysql -h "${MYSQL_HOST}" -P ${MYSQL_PORT} -u ${MYSQL_USERNAME} -B -N -e "SHOW DATABASES;"|egrep -v '^(information|performance)_schema$'); do + {{ if (eq .Values.allDatabases.singleBackupFile false) }} + {{ if and (.Values.mysql.db) (eq .Values.allDatabases.enabled false) }} + MYSQL_DBS="{{ .Values.mysql.db }}" + {{ else if (.Values.allDatabases.enabled) }} + MYSQL_DBS=$(mysql -h "${MYSQL_HOST}" -P ${MYSQL_PORT} -u ${MYSQL_USERNAME} -B -N -e "SHOW DATABASES;"|egrep -v '^(information|performance)_schema$') + {{ end }} + for MYSQL_DB in $MYSQL_DBS; do echo "Backing up db ${MYSQL_DB}" {{ if .Values.saveToDirectory }}mkdir -p "${BACKUP_DIR}"/"${MYSQL_DB}"{{ end }} mysqldump ${MYSQL_OPTS} -h ${MYSQL_HOST} -P ${MYSQL_PORT} -u ${MYSQL_USERNAME} --databases ${MYSQL_DB} | gzip > ${BACKUP_DIR}/{{ if .Values.saveToDirectory }}${MYSQL_DB}/{{ end }}${TIMESTAMP}_${MYSQL_DB}.sql.gz rc=$? done - - {{ else if and (.Values.allDatabases.enabled) (.Values.allDatabases.singleBackupFile) }} - echo "Backing up all databases" + {{ else if (.Values.allDatabases.singleBackupFile) }} MYSQL_DB="alldatabases" {{ if .Values.saveToDirectory }}mkdir -p "${BACKUP_DIR}"/"${MYSQL_DB}"{{ end }} + {{ if and (.Values.mysql.db) (eq .Values.allDatabases.enabled false) }} + echo "Backing up all this databases {{ .Values.mysql.db }}" + mysqldump ${MYSQL_OPTS} -h ${MYSQL_HOST} -P ${MYSQL_PORT} -u ${MYSQL_USERNAME} --databases {{ .Values.mysql.db }} | gzip > ${BACKUP_DIR}/{{ if .Values.saveToDirectory }}${MYSQL_DB}/{{ end }}${TIMESTAMP}_${MYSQL_DB}.sql.gz + rc=$? + {{ else if (.Values.allDatabases.enabled) }} + echo "Backing up all databases" mysqldump ${MYSQL_OPTS} -h ${MYSQL_HOST} -P ${MYSQL_PORT} -u ${MYSQL_USERNAME} --all-databases | gzip > ${BACKUP_DIR}/{{ if .Values.saveToDirectory }}${MYSQL_DB}/{{ end }}${TIMESTAMP}_${MYSQL_DB}.sql.gz rc=$? - {{- end -}} + {{ end }} + {{ end }} + - {{- if or (.Values.upload.googlestoragebucket.enabled) (.Values.upload.ssh.enabled) (.Values.upload.openstack.enabled) -}} + {{- if or (.Values.upload.googlestoragebucket.enabled) (.Values.upload.ssh.enabled) (.Values.upload.openstack.enabled) (.Values.upload.s3.enabled) -}} {{ if .Values.upload.ssh.enabled -}} echo "upload files via ssh to {{ .Values.upload.ssh.user }}@{{ .Values.upload.ssh.host }}:{{ .Values.upload.ssh.dir }}" - rsync -av --delete --exclude=*.state -e 'ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null' ${BACKUP_DIR}/ {{ .Values.upload.ssh.user }}@{{ .Values.upload.ssh.host }}:{{ .Values.upload.ssh.dir }} + rsync {{ .Values.rsync.options }} --delete --exclude=*.state -e 'ssh -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/dev/null' ${BACKUP_DIR}/ {{ .Values.upload.ssh.user }}@{{ .Values.upload.ssh.host }}:{{ .Values.upload.ssh.dir }} rcu=$? {{ end -}} @@ -85,6 +90,13 @@ data: rcu=$? {{ end }} + {{ if .Values.upload.s3.enabled -}} + echo "upload files to s3 storage bucket {{ .Values.upload.s3.bucketname }}" + aws configure set aws_access_key_id "$S3_ACCESSKEY" && aws configure set aws_secret_access_key "$S3_SECRET_KEY" && aws configure set region "$S3_REGION" + find ${BACKUP_DIR} -type d -name 'lost+found' -prune -o -maxdepth 2 -name "${TIMESTAMP}_*.sql.gz" -type f -exec aws --endpoint-url ${S3_ENDPOINT} s3 cp {} s3://${S3_BUCKETNAME} \; + rcu=$? + {{ end }} + if [ "$rcu" != "0" ]; then echo "upload failed" exit 1 diff --git a/charts/mysqldump/templates/configmap.yaml b/charts/mysqldump/templates/configmap.yaml index 3bf1c4d..b230d50 100644 --- a/charts/mysqldump/templates/configmap.yaml +++ b/charts/mysqldump/templates/configmap.yaml @@ -17,3 +17,9 @@ data: OS_USERNAME: {{ .Values.upload.openstack.user }} OS_USER_DOMAIN_NAME: {{ .Values.upload.openstack.userDomain }} {{- end }} +{{- if .Values.upload.s3.enabled }} + S3_ENDPOINT: {{ .Values.upload.s3.endpoint }} + S3_ACCESSKEY: {{ .Values.upload.s3.accesskey }} + S3_BUCKETNAME: {{ .Values.upload.s3.bucketname }} + S3_REGION: {{ .Values.upload.s3.region }} +{{- end }} \ No newline at end of file diff --git a/charts/mysqldump/templates/secret-s3.yaml b/charts/mysqldump/templates/secret-s3.yaml new file mode 100644 index 0000000..9b89a7d --- /dev/null +++ b/charts/mysqldump/templates/secret-s3.yaml @@ -0,0 +1,12 @@ +{{- if and (.Values.upload.s3.enabled) (not .Values.upload.s3.existingSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: "{{ template "mysqldump.fullname" . }}-s3-secretkey" + labels: + {{- include "mysqldump.labels" . | nindent 4 }} +type: Opaque +data: + S3_SECRET_KEY: {{ .Values.upload.s3.secretkey | b64enc | quote }} +{{- end }} + diff --git a/charts/mysqldump/values.yaml b/charts/mysqldump/values.yaml index ed3ceae..eef16d2 100644 --- a/charts/mysqldump/values.yaml +++ b/charts/mysqldump/values.yaml @@ -28,7 +28,7 @@ mysql: # no password means random value password: "" port: 3306 - # db name for single db backup + # db name for db(s) backup db: "" # Get password from existing secret existingSecret: "" @@ -37,12 +37,16 @@ mysql: # use --all-databases if enabled allDatabases: enabled: true - # creates single backup file with all databases + # creates single backup file with all databases (works with mysql.db too) singleBackupFile: false # options to pass to mysqldump options: "--opt --single-transaction" +# options to pass to rsync +rsync: + options: "-av" + # save sql backup to a directory named like the database or "alldatabases" saveToDirectory: false @@ -140,6 +144,18 @@ upload: # set to 0 to disable TTL on uploaded files ttlDays: 30 + s3: + enabled: false + bucketname: mysql-backup + endpoint: https://yourdomain/ + region: "us-east-1" + accesskey: "" + secretkey: "" + # existingSecret can be enabled to use an existing secret + existingSecret: "" + # existingSecretKey defines the key to use, or 'S3_SECRET_KEY' if not set + existingSecretKey: "" + resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little