diff --git a/Dockerfile b/Dockerfile index 4108b7e8..ad369685 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,8 @@ ENV INFLUX1_CLIENT_VERSION=1.8.0 \ INFLUX2_CLIENT_VERSION=2.7.5 \ MSODBC_VERSION=18.4.1.1-1 \ MSSQL_VERSION=18.4.1.1-1 \ + DOTNET_VERSION=8.0.413 \ + SQLPACKAGE_VERSION=170.1.61 \ MYSQL_VERSION=mysql-8.4.4 \ MYSQL_REPO_URL=https://github.com/mysql/mysql-server \ AWS_CLI_VERSION=1.36.40 \ @@ -85,6 +87,22 @@ RUN source /assets/functions/00-container && \ curl -sSLO https://download.microsoft.com/download/7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8/msodbcsql18_${MSODBC_VERSION}_${mssql_arch}.apk ; \ curl -sSLO https://download.microsoft.com/download/7/6/d/76de322a-d860-4894-9945-f0cc5d6a45f8/mssql-tools18_${MSSQL_VERSION}_${mssql_arch}.apk ; \ echo y | apk add --allow-untrusted msodbcsql18_${MSODBC_VERSION}_${mssql_arch}.apk mssql-tools18_${MSSQL_VERSION}_${mssql_arch}.apk ; \ + # Install dotnet + package install .db-backup-dotnet-deps \ + ca-certificates \ + krb5-libs \ + libgcc \ + libintl \ + libunwind \ + icu-libs \ + libssl3 \ + libstdc++ \ + zlib \ + && \ + curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --version ${DOTNET_VERSION}; \ + ln -s /root/.dotnet/ /usr/share/dotnet ; \ + # Install sqlpackage + /root/.dotnet/dotnet tool install --global Microsoft.SqlPackage --version ${SQLPACKAGE_VERSION}; \ else \ echo >&2 "Detected non x86_64 or ARM64 build variant, skipping MSSQL installation" ; \ fi; \ diff --git a/README.md b/README.md index 796986f8..03e08769 100644 --- a/README.md +++ b/README.md @@ -285,7 +285,7 @@ Encryption occurs after compression and the encrypted filename will have a `.gpg | Variable | Description | Default | `_FILE` | | -------------------- | --------------------------------------- | ---------- | ------- | | `DEFAULT_PORT` | Microsoft SQL Port | `1433` | x | -| `DEFAULT_MSSQL_MODE` | Backup `DATABASE` or `TRANSACTION` logs | `DATABASE` | +| `DEFAULT_MSSQL_MODE` | Backup `DATABASE` or `TRANSACTION` logs or `SQLPACKAGE` to backup a dacpac file | `DATABASE` | ###### MongoDB @@ -559,7 +559,7 @@ Encryption will occur after compression and the resulting filename will have a ` | Variable | Description | Default | `_FILE` | | ----------------- | --------------------------------------- | ---------- | ------- | | `DB01_PORT` | Microsoft SQL Port | `1433` | x | -| `DB01_MSSQL_MODE` | Backup `DATABASE` or `TRANSACTION` logs | `DATABASE` | +| `DB01_MSSQL_MODE` | Backup `DATABASE` or `TRANSACTION` logs or `SQLPACKAGE` to backup a dacpac file | `DATABASE` | ###### MongoDB diff --git a/examples/mssql-sqlpackage/compose.yml b/examples/mssql-sqlpackage/compose.yml new file mode 100644 index 00000000..07216772 --- /dev/null +++ b/examples/mssql-sqlpackage/compose.yml @@ -0,0 +1,58 @@ +# +# Example for Microsoft SQL Server +# + +services: + example-mssql-db: + hostname: example-db-host + image: mcr.microsoft.com/mssql/server:2019-latest + container_name: example-mssql-db + restart: unless-stopped + ports: + - "127.0.0.1:11433:1433" + networks: + example-mssql-net: + environment: + ACCEPT_EULA: Y + MSSQL_SA_PASSWORD: 5hQa0utRFBpIY3yhoIyE + MSSQL_PID: Express + + example-mssql-db-backup: + container_name: example-mssql-db-backup + # if you want to build and use image from current source + # execute in terminal --> docker build -t tiredofit/db-backup-mssql . + # replace --> image: tiredofit/db-backup-mssql + # image: tiredofit/db-backup + image: tiredofit/db-backup + links: + - example-mssql-db + volumes: + - ./backups:/backup + #- ./post-script.sh:/assets/custom-scripts/post-script.sh + environment: + - TIMEZONE=America/Vancouver + - CONTAINER_ENABLE_MONITORING=FALSE + - CONTAINER_NAME=example-mssql-sqlpackage-db-backup + + # - DEBUG_MODE=TRUE + - DB01_TYPE=mssql + - DB01_MSSQL_MODE=sqlpackage + - DB01_HOST=example-db-host + # - DB_PORT=1488 + # - DB_NAME=ALL # [ALL] not working on sql server. + # create database with name `test1` manually first + - DB01_NAME=test1 + - DB01_USER=sa + - DB01_PASS=5hQa0utRFBpIY3yhoIyE + - DB01_BACKUP_INTERVAL=1 # backup every minute + # - DB01_DUMP_BEGIN=0000 # backup starts at midnight vs unset immediately + - DB01_CLEANUP_TIME=5 # clean backups they are older than 5 minute + - DB01_CHECKSUM=NONE + - DB01_COMPRESSION=GZ + restart: always + networks: + example-mssql-net: + +networks: + example-mssql-net: + name: example-mssql-net diff --git a/install/assets/functions/10-db-backup b/install/assets/functions/10-db-backup index 63aec237..0339ba4e 100644 --- a/install/assets/functions/10-db-backup +++ b/install/assets/functions/10-db-backup @@ -674,6 +674,30 @@ backup_mssql() { post_dbbackup "${backup_job_db_name}" cleanup_old_data ;; + sqlpackage ) + prepare_dbbackup + backup_job_filename_base=mssql_${backup_job_db_name,,}_${backup_job_db_host,,} + backup_job_filename_base=$(sed 's/ /-/g' <<< "${backup_job_filename_base}") + backup_job_filename=${backup_job_filename_base}_${now}.dacpac + pre_dbbackup "${backup_job_db_name}" + write_log notice "Dumping MSSQL database: '${backup_job_db_name}'" + if var_true "${DEBUG_BACKUP_MSSQL}" ; then debug on; fi + silent ${play_fair} /root/.dotnet/tools/sqlpackage /Action:Extract /SourceConnectionString:"Data Source=${backup_job_db_host},${backup_job_db_port};Database='${backup_job_db_name}';User Id='${backup_job_db_user}';Password='${backup_job_db_pass}';Encrypt=False;" /p:ExtractApplicationScopedObjectsOnly=false /p:VerifyExtraction=true /p:ExtractAllTableData=true /TargetFile:"${temporary_directory}/${backup_job_filename}" + exit_code=$? + silent chown "${DBBACKUP_USER}":"${DBBACKUP_GROUP}" "${temporary_directory}/${backup_job_filename}" + if var_true "${DEBUG_BACKUP_MSSQL}" ; then debug off; fi + backup_job_filename_original=${backup_job_filename} + compression + pre_dbbackup all + run_as_user ${compress_cmd} "${temporary_directory}/${backup_job_filename_original}" + file_encryption + timer backup finish + generate_checksum + move_dbbackup + check_exit_code move "${backup_job_filename}" + post_dbbackup "${backup_job_db_name}" + cleanup_old_data + ;; trn|transaction ) prepare_dbbackup backup_job_filename=mssql_${backup_job_db_name,,}_${backup_job_db_host,,}_${now}.trn diff --git a/install/usr/local/bin/restore b/install/usr/local/bin/restore index 7a31994c..e7a8a98a 100755 --- a/install/usr/local/bin/restore +++ b/install/usr/local/bin/restore @@ -619,6 +619,11 @@ get_dbtype() { DEFAULT_PORT=${DEFAULT_PORT:-"5432"} print_debug "Parsed DBType: Postgresql" ;; + mssql | sqlserver ) + parsed_type=true + DEFAULT_PORT=${DEFAULT_PORT:-"1433"} + print_debug "Parsed DBType: Microsoft SQL" + ;; * ) print_debug "Parsed DBType: UNKNOWN" ;; @@ -672,6 +677,7 @@ ${q_dbtype_menu} M ) MySQL / MariaDB O ) MongoDB P ) Postgresql + S ) Microsoft SQL Q ) Quit EOF @@ -696,6 +702,11 @@ EOF DEFAULT_PORT=${DEFAULT_PORT:-"5432"} break ;; + s ) + r_dbtype=sqlserver + DEFAULT_PORT=${DEFAULT_PORT:-"1433"} + break + ;; q* ) print_info "Quitting Script" exit 1 @@ -726,6 +737,11 @@ EOF DEFAULT_PORT=${DEFAULT_PORT:-"5432"} break ;; + s* ) + r_dbtype=sqlserver + DEFAULT_PORT=${DEFAULT_PORT:-"1433"} + break + ;; q* ) print_info "Quitting Script" exit 1 @@ -767,6 +783,11 @@ EOF DEFAULT_PORT=${DEFAULT_PORT:-"5432"} break ;; + s* ) + r_dbtype=sqlserver + DEFAULT_PORT=${DEFAULT_PORT:-"1433"} + break + ;; q* ) print_info "Quitting Script" exit 1 @@ -802,6 +823,11 @@ EOF DEFAULT_PORT=${DEFAULT_PORT:-"5432"} break ;; + s* ) + r_dbtype=sqlserver + DEFAULT_PORT=${DEFAULT_PORT:-"1433"} + break + ;; q* ) print_info "Quitting Script" exit 1 @@ -1119,6 +1145,14 @@ case "${r_dbtype}" in pv ${r_filename} | ${decompress_cmd}cat | psql -d ${r_dbname} -h ${r_dbhost} -p ${r_dbport} -U ${r_dbuser} exit_code=$? ;; + mssql | sqlserver ) + print_info "Restoring '${r_filename}' into '${r_dbhost}'/'${r_dbname}'" + temp_filename=$(mktemp).dacpac + pv ${r_filename} | ${decompress_cmd}cat > ${temp_filename} + silent ${play_fair} /root/.dotnet/tools/sqlpackage /Action:Publish /SourceFile:"${temp_filename}" /TargetConnectionString:"Data Source=${r_dbhost},${r_dbport};Database=${r_dbname};User Id=${r_dbuser};Password=${r_dbpass};Encrypt=False;" + exit_code=$? + rm ${temp_filename} + ;; mongo ) cat << EOF