Revised backup logic
parent
d123a32517
commit
71b172aeac
44
manifest.jps
44
manifest.jps
|
@ -19,7 +19,15 @@ onInstall:
|
|||
- checkApplication
|
||||
- checkAddons
|
||||
- installRestic
|
||||
- setSchedule
|
||||
|
||||
menu:
|
||||
confirmText: Backup ALL Now?
|
||||
loadingText: Backing up ALL data on your site
|
||||
action: backupnow
|
||||
caption: Backup All
|
||||
successText: Backup All Completed Successfully
|
||||
title: Backup All Now
|
||||
submitButtonText: Backup Now
|
||||
|
||||
onUninstall:
|
||||
- callScript: uninstall
|
||||
|
@ -40,8 +48,6 @@ onAfterClone:
|
|||
cronTime: "0 * * * *"
|
||||
backupCount: "5"
|
||||
|
||||
onAfterConfirmTransfer: setSchedule
|
||||
|
||||
actions:
|
||||
checkApplication:
|
||||
- cmd[${nodes.cp.master.id}]: |-
|
||||
|
@ -53,6 +59,8 @@ actions:
|
|||
- stopEvent:
|
||||
type: warning
|
||||
message: Deployed application is not supported by Backup add-on.
|
||||
backupnow:
|
||||
message: BACKUP ALL DONE
|
||||
|
||||
checkAddons:
|
||||
- script: |-
|
||||
|
@ -89,7 +97,14 @@ actions:
|
|||
yum-config-manager --enable copr:copr.fedorainfracloud.org:copart:restic
|
||||
yum -y install restic
|
||||
yum-config-manager --disable copr:copr.fedorainfracloud.org:copart:restic
|
||||
wget -O /etc/logrotate.d/backup-addon ${baseUrl}/scripts/backup-addon;
|
||||
echo "/var/log/backup_addon.log {
|
||||
weekly
|
||||
rotate 52
|
||||
missingok
|
||||
notifempty
|
||||
compress
|
||||
copytruncate
|
||||
}" > /etc/logrotate.d/backup-addon
|
||||
fi
|
||||
user: root
|
||||
|
||||
|
@ -100,12 +115,9 @@ actions:
|
|||
params:
|
||||
scriptName: ${env.envName}-${globals.scriptSufix}
|
||||
baseUrl: ${baseUrl}
|
||||
cronTime: '${this.cronTime}'
|
||||
backupCount: ${this.backupCount}
|
||||
cronTime: '0 * * * *'
|
||||
backupCount: '5'
|
||||
userId: ${env.uid}
|
||||
storageNodeId: ${response.storageCtid}
|
||||
backupExecNode: ${nodes.cp.master.id}
|
||||
storageEnv: ${response.storageEnvShortName}
|
||||
|
||||
callScript:
|
||||
script: |-
|
||||
|
@ -134,17 +146,3 @@ actions:
|
|||
echo "${settings.backupDir}" > /root/.backupid
|
||||
user: root
|
||||
- callScript: restore
|
||||
|
||||
configure:
|
||||
- setSchedule
|
||||
|
||||
getStorageCtid:
|
||||
- script: scripts/getStorageCtid.js
|
||||
|
||||
setSchedule:
|
||||
- setGlobals:
|
||||
cron: ${settings.cronTime}
|
||||
- installScript:
|
||||
cronTime: ${globals.cron}
|
||||
backupCount: 5
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
#!/bin/bash
|
||||
|
||||
BASE_URL=$2
|
||||
BACKUP_TYPE=$3
|
||||
BACKUP_LOG_FILE=$4
|
||||
ENV_NAME=$5
|
||||
BACKUP_COUNT=$6
|
||||
APP_PATH=$7
|
||||
USER_SESSION=$8
|
||||
USER_EMAIL=$9
|
||||
BACKUP_PATH=${10}
|
||||
|
||||
# Ensure restic is installed
|
||||
if ! which restic &>/dev/null; then
|
||||
if which dnf &>/dev/null; then
|
||||
dnf install -y epel-release
|
||||
dnf install -y restic
|
||||
elif which yum &>/dev/null; then
|
||||
yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-7/copart-restic-epel-7.repo
|
||||
yum-config-manager --enable copart-restic
|
||||
yum -y install restic
|
||||
yum-config-manager --disable copart-restic
|
||||
fi
|
||||
fi
|
||||
|
||||
# Function definitions remain the same, but adjustments are made to use $BACKUP_PATH
|
||||
function update_restic(){
|
||||
restic self-update 2>&1;
|
||||
}
|
||||
|
||||
function check_backup_repo(){
|
||||
local backup_repo_path="/mnt/backups/${ENV_NAME}"
|
||||
[ -d "${backup_repo_path}" ] || mkdir -p "${backup_repo_path}"
|
||||
export FILES_COUNT=$(find "${backup_repo_path}" -mindepth 1 | wc -l)
|
||||
|
||||
if [ "${FILES_COUNT}" -gt 0 ]; then
|
||||
echo "$(date) ${ENV_NAME} Checking the backup repository integrity and consistency" | tee -a "${BACKUP_LOG_FILE}"
|
||||
|
||||
if [[ $(ls -A "${backup_repo_path}/locks") ]]; then
|
||||
echo "$(date) ${ENV_NAME} Backup repository has a stale lock, removing" | tee -a "${BACKUP_LOG_FILE}"
|
||||
GOGC=20 RESTIC_PASSWORD="${ENV_NAME}" restic -r "${backup_repo_path}" unlock
|
||||
sendEmailNotification
|
||||
fi
|
||||
|
||||
if ! GOGC=20 RESTIC_PASSWORD="${ENV_NAME}" restic -q -r "${backup_repo_path}" check --read-data-subset=5%; then
|
||||
echo "Backup repository integrity check failed." | tee -a "${BACKUP_LOG_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
echo "$(date) ${ENV_NAME} Initializing new backup repository" | tee -a "${BACKUP_LOG_FILE}"
|
||||
GOGC=20 RESTIC_PASSWORD="${ENV_NAME}" restic init -r "${backup_repo_path}"
|
||||
fi
|
||||
}
|
||||
|
||||
function sendEmailNotification() {
|
||||
if [ -e "/usr/lib/jelastic/modules/api.module" ]; then
|
||||
[ -e "/var/run/jem.pid" ] && return 0;
|
||||
CURRENT_PLATFORM_MAJOR_VERSION=$(jem api apicall -s --connect-timeout 3 --max-time 15 [API_DOMAIN]/1.0/statistic/system/rest/getversion 2>/dev/null | jq .version | grep -o [0-9.]* | awk -F . '{print $1}')
|
||||
if [ "${CURRENT_PLATFORM_MAJOR_VERSION}" -ge "7" ]; then
|
||||
echo "$(date) ${ENV_NAME} Sending e-mail notification about removing the stale lock" | tee -a "$BACKUP_LOG_FILE"
|
||||
SUBJECT="Stale lock is removed on ${BACKUP_PATH}/${ENV_NAME} backup repo"
|
||||
BODY="Please pay attention to ${BACKUP_PATH}/${ENV_NAME} backup repo because the stale lock left from previous operation is removed during the integrity check and backup rotation. Manual check of backup repo integrity and consistency is highly desired."
|
||||
jem api apicall -s --connect-timeout 3 --max-time 15 [API_DOMAIN]/1.0/message/email/rest/send --data-urlencode "session=$USER_SESSION" --data-urlencode "to=$USER_EMAIL" --data-urlencode "subject=$SUBJECT" --data-urlencode "body=$BODY"
|
||||
if [[ $? != 0 ]]; then
|
||||
echo "$(date) ${ENV_NAME} Sending of e-mail notification failed" | tee -a "$BACKUP_LOG_FILE"
|
||||
else
|
||||
echo "$(date) ${ENV_NAME} E-mail notification is sent successfully" | tee -a "$BACKUP_LOG_FILE"
|
||||
fi
|
||||
elif [ -z "${CURRENT_PLATFORM_MAJOR_VERSION}" ]; then
|
||||
echo "$(date) ${ENV_NAME} Error when checking the platform version" | tee -a "$BACKUP_LOG_FILE"
|
||||
else
|
||||
echo "$(date) ${ENV_NAME} Email notification is not sent because this functionality is unavailable for current platform version." | tee -a "$BACKUP_LOG_FILE"
|
||||
fi
|
||||
else
|
||||
echo "$(date) ${ENV_NAME} Email notification is not sent because this functionality is unavailable for current platform version." | tee -a "$BACKUP_LOG_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
function rotate_snapshots(){
|
||||
local backup_repo_path="/mnt/backups/${ENV_NAME}"
|
||||
echo "$(date) ${ENV_NAME} Rotating snapshots by keeping the last ${BACKUP_COUNT}" | tee -a "${BACKUP_LOG_FILE}"
|
||||
|
||||
if [[ $(ls -A "${backup_repo_path}/locks") ]]; then
|
||||
echo "$(date) ${ENV_NAME} Backup repository has a stale lock, removing" | tee -a "${BACKUP_LOG_FILE}"
|
||||
GOGC=20 RESTIC_PASSWORD="${ENV_NAME}" restic -r "${backup_repo_path}" unlock
|
||||
sendEmailNotification
|
||||
fi
|
||||
|
||||
if ! GOGC=20 RESTIC_COMPRESSION=off RESTIC_PACK_SIZE=8 RESTIC_PASSWORD="${ENV_NAME}" restic forget -q -r "${backup_repo_path}" --keep-last "${BACKUP_COUNT}" --prune | tee -a "${BACKUP_LOG_FILE}"; then
|
||||
echo "Backup rotation failed." | tee -a "${BACKUP_LOG_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
function create_snapshot(){
|
||||
local backup_repo_path="/mnt/backups/${ENV_NAME}"
|
||||
DUMP_NAME=$(date "+%F_%H%M%S_%Z")
|
||||
echo "$(date) ${ENV_NAME} Begin uploading the ${DUMP_NAME} snapshot to backup storage" | tee -a "${BACKUP_LOG_FILE}"
|
||||
|
||||
if ! GOGC=20 RESTIC_COMPRESSION=off RESTIC_PACK_SIZE=8 RESTIC_READ_CONCURRENCY=8 RESTIC_PASSWORD="${ENV_NAME}" restic backup -q -r "${backup_repo_path}" --tag "${DUMP_NAME} ${BACKUP_ADDON_COMMIT_ID} ${BACKUP_TYPE}" "${APP_PATH}" ~/wp_db_backup.sql | tee -a "${BACKUP_LOG_FILE}"; then
|
||||
echo "Backup snapshot creation failed." | tee -a "${BACKUP_LOG_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$(date) ${ENV_NAME} End uploading the ${DUMP_NAME} snapshot to backup storage" | tee -a "${BACKUP_LOG_FILE}"
|
||||
}
|
||||
|
||||
function backup(){
|
||||
local backup_repo_path="/mnt/backups/${ENV_NAME}"
|
||||
echo $$ > "/var/run/${ENV_NAME}_backup.pid"
|
||||
BACKUP_ADDON_REPO=$(echo "${BASE_URL}" | sed 's|https://raw.githubusercontent.com/||' | awk -F '/' '{print $1"/"$2}')
|
||||
BACKUP_ADDON_BRANCH=$(echo "${BASE_URL}" | sed 's|https://raw.githubusercontent.com/||' | awk -F '/' '{print $3}')
|
||||
BACKUP_ADDON_COMMIT_ID=$(git ls-remote "https://github.com/${BACKUP_ADDON_REPO}.git" | grep "/${BACKUP_ADDON_BRANCH}$" | awk '{print $1}')
|
||||
echo "$(date) ${ENV_NAME} Creating the ${BACKUP_TYPE} backup (using the backup addon with commit id ${BACKUP_ADDON_COMMIT_ID})" | tee -a "${BACKUP_LOG_FILE}"
|
||||
|
||||
# Database credentials and dump
|
||||
source /var/www/webroot/ROOT/wp-config.php
|
||||
DB_HOST=$(echo "${DB_HOST}" | awk -F ':' '{print $1}')
|
||||
DB_PORT=$(echo "${DB_HOST}" | awk -F ':' '{print $2:-3306}')
|
||||
|
||||
echo "$(date) ${ENV_NAME} Creating the DB dump" | tee -a "${BACKUP_LOG_FILE}"
|
||||
if ! mysqldump -h "${DB_HOST}" -P "${DB_PORT}" -u "${DB_USER}" -p"${DB_PASSWORD}" "${DB_NAME}" --force --single-transaction --quote-names --opt --databases > "${backup_repo_path}/wp_db_backup.sql"; then
|
||||
echo "$(date) ${ENV_NAME} DB backup process failed." | tee -a "${BACKUP_LOG_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Backup process
|
||||
if ! restic -r "${backup_repo_path}" backup "${backup_repo_path}/wp_db_backup.sql" "${APP_PATH}" --tag "${BACKUP_TYPE}" --host "${ENV_NAME}"; then
|
||||
echo "$(date) ${ENV_NAME} Backup process failed." | tee -a "${BACKUP_LOG_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$(date) ${ENV_NAME} Backup process completed successfully." | tee -a "${BACKUP_LOG_FILE}"
|
||||
rm -f "/var/run/${ENV_NAME}_backup.pid"
|
||||
}
|
||||
|
||||
function backup_wp_core() {
|
||||
echo "Starting backup of WordPress core files excluding uploads directory..."
|
||||
# Define the path to the WordPress installation and the backup tag
|
||||
local wp_path="$APP_PATH"
|
||||
local backup_tag="wp-core-$(date "+%F_%H%M%S_%Z")"
|
||||
|
||||
# Exclude the uploads directory using the --exclude option
|
||||
GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \
|
||||
--tag "$backup_tag" \
|
||||
--exclude="$wp_path/wp-content/uploads" \
|
||||
"$wp_path"
|
||||
|
||||
echo "WordPress core files backup completed."
|
||||
}
|
||||
|
||||
function backup_uploads() {
|
||||
echo "Starting backup of WordPress uploads directory..."
|
||||
# Define the path to the uploads directory and the backup tag
|
||||
local uploads_path="$APP_PATH/wp-content/uploads"
|
||||
local backup_tag="wp-uploads-$(date "+%F_%H%M%S_%Z")"
|
||||
|
||||
# Perform the backup
|
||||
GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \
|
||||
--tag "$backup_tag" \
|
||||
"$uploads_path"
|
||||
|
||||
echo "WordPress uploads directory backup completed."
|
||||
}
|
||||
|
||||
function backup_database() {
|
||||
echo "Starting backup of WordPress database..."
|
||||
# Define the backup tag and the path for the temporary SQL dump
|
||||
local backup_tag="wp-db-$(date "+%F_%H%M%S_%Z")"
|
||||
local sql_dump_path="/tmp/wp_db_backup.sql"
|
||||
|
||||
# Create a database dump
|
||||
mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASSWORD" "$DB_NAME" > "$sql_dump_path"
|
||||
|
||||
# Perform the backup
|
||||
GOGC=20 RESTIC_PASSWORD="$RESTIC_PASSWORD" restic -r "$backupPath" backup \
|
||||
--tag "$backup_tag" \
|
||||
"$sql_dump_path"
|
||||
|
||||
# Remove the temporary SQL dump file
|
||||
rm -f "$sql_dump_path"
|
||||
echo "WordPress database backup completed."
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
backup)
|
||||
backup_wp_core
|
||||
backup_uploads
|
||||
backup_database
|
||||
;;
|
||||
backup_wp_core)
|
||||
backup_wp_core
|
||||
;;
|
||||
backup_uploads)
|
||||
backup_uploads
|
||||
;;
|
||||
backup_database)
|
||||
backup_database
|
||||
;;
|
||||
check_backup_repo)
|
||||
check_backup_repo
|
||||
;;
|
||||
rotate_snapshots)
|
||||
rotate_snapshots
|
||||
;;
|
||||
create_snapshot)
|
||||
create_snapshot
|
||||
;;
|
||||
update_restic)
|
||||
update_restic
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {backup|backup_wp_core|backup_uploads|backup_database|check_backup_repo|rotate_snapshots|create_snapshot|update_restic}"
|
||||
exit 2
|
||||
esac
|
||||
|
||||
|
||||
exit $?
|
|
@ -12,10 +12,8 @@ function run() {
|
|||
scriptName : "${scriptName}",
|
||||
envName : "${envName}",
|
||||
envAppid : "${envAppid}",
|
||||
storageNodeId : "${storageNodeId}",
|
||||
backupExecNode : "${backupExecNode}",
|
||||
backupCount : "${backupCount}",
|
||||
storageEnv : "${storageEnv}"
|
||||
backupPath : "/mnt/backup"
|
||||
});
|
||||
|
||||
api.local.ReturnResult(
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
if [ -e /var/www/webroot/ROOT/wp-config.php ]; then
|
||||
echo "$(date) trying to install the backup add-on" >> /var/log/backup_addon.log
|
||||
if [ -e /home/jelastic/bin/wp ]; then
|
||||
/home/jelastic/bin/wp --info >> /var/log/backup_addon.log
|
||||
fi
|
||||
else
|
||||
echo "$(date) The application deployed to WEBROOT cannot be backuped by Jelastic backup add-on" >> /var/log/backup_addon.log
|
||||
echo "Non-supported"
|
||||
fi
|
|
@ -1,89 +0,0 @@
|
|||
function BackupManager(config) {
|
||||
var Response = com.hivext.api.Response,
|
||||
Transport = com.hivext.api.core.utils.Transport,
|
||||
Logger = org.slf4j.LoggerFactory.getLogger("backup-addon:" + config.envName);
|
||||
|
||||
var me = this;
|
||||
var BACKUP_TYPES = {
|
||||
CORE: "core",
|
||||
MEDIA: "media",
|
||||
DATABASE: "db"
|
||||
};
|
||||
|
||||
me.invoke = function(action) {
|
||||
var actions = {
|
||||
"backup": me.backup,
|
||||
"restore": me.restore
|
||||
};
|
||||
|
||||
return actions[action] ? actions[action].call(me) : {
|
||||
result: Response.ERROR_UNKNOWN,
|
||||
error: "Unknown action: " + action
|
||||
};
|
||||
};
|
||||
|
||||
me.backup = function() {
|
||||
// Sequentially perform all backups
|
||||
me.backupCoreFiles();
|
||||
me.backupMediaFiles();
|
||||
me.backupDatabase();
|
||||
Logger.info("All backups completed successfully.");
|
||||
};
|
||||
|
||||
me.backupCoreFiles = function() {
|
||||
var backupName = me.getBackupName(BACKUP_TYPES.CORE);
|
||||
// Placeholder for core files backup logic
|
||||
Logger.info("Core files backup completed: " + backupName);
|
||||
};
|
||||
|
||||
me.backupMediaFiles = function() {
|
||||
var backupName = me.getBackupName(BACKUP_TYPES.ME DIA);
|
||||
// Placeholder for media files backup logic
|
||||
Logger.info("Media files backup completed: " + backupName);
|
||||
};
|
||||
|
||||
me.backupDatabase = function() {
|
||||
var backupName = me.getBackupName(BACKUP_TYPES.DATABASE);
|
||||
// Placeholder for database backup logic
|
||||
Logger.info("Database backup completed: " + backupName);
|
||||
};
|
||||
|
||||
me.restore = function(backupName) {
|
||||
// Determine type from backupName and call the respective restore function
|
||||
var type = backupName.split("_")[1]; // Assumes naming convention is used
|
||||
switch(type) {
|
||||
case BACKUP_TYPES.CORE:
|
||||
me.restoreCoreFiles(backupName);
|
||||
break;
|
||||
case BACKUP_TYPES.MEDIA:
|
||||
me.restoreMediaFiles(backupName);
|
||||
break;
|
||||
case BACKUP_TYPES.DATABASE:
|
||||
me.restoreDatabase(backupName);
|
||||
break;
|
||||
default:
|
||||
Logger.error("Unknown backup type for restoration: " + backupName);
|
||||
}
|
||||
};
|
||||
|
||||
me.restoreCoreFiles = function(backupName) {
|
||||
// Placeholder for core files restore logic
|
||||
Logger.info("Core files restoration completed: " + backupName);
|
||||
};
|
||||
|
||||
me.restoreMediaFiles = function(backupName) {
|
||||
// Placeholder for media files restore logic
|
||||
Logger.info("Media files restoration completed: " + backupName);
|
||||
};
|
||||
|
||||
me.restoreDatabase = function(backupName) {
|
||||
// Placeholder for database restore logic
|
||||
Logger.info("Database restoration completed: " + backupName);
|
||||
};
|
||||
|
||||
me.getBackupName = function(type) {
|
||||
var dateFormat = new java.text.SimpleDateFormat("yyyyMMddHHmmss");
|
||||
var dateStr = dateFormat.format(new java.util.Date());
|
||||
return config.envName + "_" + type + "_" + dateStr;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,454 @@
|
|||
function BackupManager(config) {
|
||||
/**
|
||||
* Implements backup management of the environment data
|
||||
* @param {{
|
||||
* session : {String}
|
||||
* baseUrl : {String}
|
||||
* uid : {Number}
|
||||
* cronTime : {String}
|
||||
* scriptName : {String}
|
||||
* envName : {String}
|
||||
* envAppid : {String}
|
||||
* [backupCount] : {String}
|
||||
* }} config
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
var Response = com.hivext.api.Response,
|
||||
EnvironmentResponse = com.hivext.api.environment.response.EnvironmentResponse,
|
||||
ScriptEvalResponse = com.hivext.api.development.response.ScriptEvalResponse,
|
||||
Transport = com.hivext.api.core.utils.Transport,
|
||||
Random = com.hivext.api.utils.Random,
|
||||
SimpleDateFormat = java.text.SimpleDateFormat,
|
||||
StrSubstitutor = org.apache.commons.lang3.text.StrSubstitutor,
|
||||
Scripting = com.hivext.api.development.Scripting,
|
||||
LoggerFactory = org.slf4j.LoggerFactory,
|
||||
LoggerName = "scripting.logger.backup-addon:" + config.envName,
|
||||
Logger = LoggerFactory.getLogger(LoggerName),
|
||||
|
||||
me = this,
|
||||
nodeManager, session;
|
||||
|
||||
config = config || {};
|
||||
session = config.session;
|
||||
nodeManager = new NodeManager(config.envName);
|
||||
|
||||
me.invoke = function (action) {
|
||||
var actions = {
|
||||
"install": me.install,
|
||||
"uninstall": me.uninstall,
|
||||
"backup": me.backup,
|
||||
"restore": me.restore,
|
||||
"backup_wp_core": me.backup_wp_core,
|
||||
"backup_uploads": me.backup_uploads,
|
||||
"backup_database": me.backup_database
|
||||
};
|
||||
|
||||
if (!actions[action]) {
|
||||
return {
|
||||
result: Response.ERROR_UNKNOWN,
|
||||
error: "unknown action [" + action + "]"
|
||||
}
|
||||
}
|
||||
|
||||
return actions[action].call(me);
|
||||
};
|
||||
|
||||
me.install = function () {
|
||||
var resp;
|
||||
|
||||
return me.exec([
|
||||
[me.cmd, ['echo $(date) %(envName) "Creating the backup task for %(envName) with the backup count %(backupCount), backup schedule %(cronTime)" | tee -a %(backupLogFile)'],
|
||||
{
|
||||
envName: config.envName,
|
||||
cronTime: config.cronTime,
|
||||
backupCount: config.backupCount,
|
||||
backupLogFile: "/var/log/backup_addon.log"
|
||||
}],
|
||||
[me.createScript],
|
||||
[me.clearScheduledBackups],
|
||||
[me.scheduleBackup]
|
||||
]);
|
||||
};
|
||||
|
||||
me.uninstall = function () {
|
||||
return me.exec(me.clearScheduledBackups);
|
||||
};
|
||||
|
||||
me.checkCurrentlyRunningBackup = function () {
|
||||
var resp = me.exec([
|
||||
[me.cmd, ['pgrep -f "%(envName)"_backup-logic.sh 1>/dev/null && echo "Running"; true'],
|
||||
{
|
||||
envName: config.envName
|
||||
}]
|
||||
]);
|
||||
if (resp.responses[0].out == "Running") {
|
||||
return {
|
||||
result: Response.ERROR_UNKNOWN,
|
||||
error: "Another backup process is already running"
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
"result": 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
me.backup = function () {
|
||||
var backupType = !getParam("task") ? "manual" : "auto";
|
||||
|
||||
var backupCallParams = {
|
||||
envName: config.envName,
|
||||
appPath: "/var/www/webroot/ROOT",
|
||||
backupCount: config.backupCount,
|
||||
backupLogFile: "/var/log/backup_addon.log",
|
||||
baseUrl: config.baseUrl,
|
||||
backupType: backupType,
|
||||
session: session,
|
||||
email: user.email,
|
||||
backupPath: "/mnt/backup" // Direct path to mounted backup storage
|
||||
};
|
||||
|
||||
return me.exec([
|
||||
[me.checkEnvStatus],
|
||||
[me.checkCurrentlyRunningBackup],
|
||||
[me.cmd, [
|
||||
'[ -f /root/%(envName)_backup-logic.sh ] && rm -f /root/%(envName)_backup-logic.sh || true',
|
||||
'wget -O /root/%(envName)_backup-logic.sh %(baseUrl)/scripts/backup-logic.sh'
|
||||
], {
|
||||
nodeGroup: "cp",
|
||||
envName: config.envName,
|
||||
baseUrl: config.baseUrl
|
||||
}],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh update_restic'
|
||||
], {
|
||||
nodeGroup: "cp",
|
||||
envName: config.envName
|
||||
} ],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh check_backup_repo %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(backupCount) %(appPath) %(session) %(email)'
|
||||
], backupCallParams ],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh backup %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(backupCount) %(appPath)'
|
||||
], backupCallParams ],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh create_snapshot %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(backupCount) %(appPath)'
|
||||
], backupCallParams ],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh rotate_snapshots %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(backupCount) %(appPath) %(session) %(email)'
|
||||
], backupCallParams ],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh check_backup_repo %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(backupCount) %(appPath) %(session) %(email)'
|
||||
], backupCallParams ]
|
||||
]);
|
||||
};
|
||||
|
||||
me.restore = function () {
|
||||
return me.exec([
|
||||
[me.checkEnvStatus],
|
||||
[me.checkCurrentlyRunningBackup],
|
||||
[me.addMountForRestore],
|
||||
[me.cmd, ['echo $(date) %(envName) Restoring the snapshot $(cat /root/.backupid)', 'restic self-update 2>&1', 'if [ -e /root/.backupedenv ]; then REPO_DIR=$(cat /root/.backupedenv); else REPO_DIR="%(envName)"; fi', 'jem service stop', 'SNAPSHOT_ID=$(RESTIC_PASSWORD=$REPO_DIR restic -r /opt/backup/$REPO_DIR snapshots|grep $(cat /root/.backupid)|awk \'{print $1}\')', '[ -n "${SNAPSHOT_ID}" ] || false', 'RESTIC_PASSWORD=$REPO_DIR GOGC=20 restic -r /opt/backup/$REPO_DIR restore ${SNAPSHOT_ID} --target /'],
|
||||
{
|
||||
nodeGroup: "cp",
|
||||
envName: config.envName
|
||||
}],
|
||||
[me.cmd, [
|
||||
'echo $(date) %(envName) Restoring the database from snapshot $(cat /root/.backupid)',
|
||||
'! which mysqld || service mysql start 2>&1',
|
||||
'for i in DB_HOST DB_USER DB_PASSWORD DB_NAME; do declare "${i}"=$(cat %(appPath)/wp-config.php | grep ${i} |grep -v \'^[[:space:]]*#\' | tr -d \'[[:blank:]]\' | awk -F \',\' \'{print $2}\' | tr -d "\\"\');"|tr -d \'\\r\'|tail -n 1); done',
|
||||
'source /etc/jelastic/metainf.conf ; if [ "${COMPUTE_TYPE}" == "lemp" -o "${COMPUTE_TYPE}" == "llsmp" ]; then wget -O /root/addAppDbUser.sh %(baseUrl)/scripts/addAppDbUser.sh; chmod +x /root/addAppDbUser.sh; bash /root/addAppDbUser.sh ${DB_USER} ${DB_PASSWORD} ${DB_HOST}; fi',
|
||||
'mysql -u${DB_USER} -p${DB_PASSWORD} -h ${DB_HOST} --execute="CREATE DATABASE IF NOT EXISTS ${DB_NAME};"', 'mysql -h ${DB_HOST} -u ${DB_USER} -p${DB_PASSWORD} ${DB_NAME} --force < /root/wp_db_backup.sql'
|
||||
],
|
||||
{
|
||||
envName: config.envName,
|
||||
baseUrl: config.baseUrl,
|
||||
appPath: "/var/www/webroot/ROOT"
|
||||
}],
|
||||
[me.cmd, ['rm -f /root/.backupid /root/wp_db_backup.sql', 'jem service start'],
|
||||
{
|
||||
nodeGroup: "cp",
|
||||
envName: config.envName
|
||||
}]
|
||||
]);
|
||||
}
|
||||
|
||||
me.checkEnvStatus = function checkEnvStatus() {
|
||||
if (!nodeManager.isEnvRunning()) {
|
||||
return {
|
||||
result: EnvironmentResponse.ENVIRONMENT_NOT_RUNNING,
|
||||
error: _("env [%(name)] not running", {
|
||||
name: config.envName
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
result: 0
|
||||
};
|
||||
};
|
||||
|
||||
me.createScript = function createScript() {
|
||||
var url = me.getScriptUrl("backup-main.js"),
|
||||
scriptName = config.scriptName,
|
||||
scriptBody, resp;
|
||||
|
||||
try {
|
||||
scriptBody = new Transport().get(url);
|
||||
|
||||
scriptBody = me.replaceText(scriptBody, config);
|
||||
|
||||
//delete the script if it already exists
|
||||
api.dev.scripting.DeleteScript(scriptName);
|
||||
|
||||
//create a new script
|
||||
resp = api.dev.scripting.CreateScript(scriptName, "js", scriptBody);
|
||||
|
||||
java.lang.Thread.sleep(1000);
|
||||
|
||||
//build script to avoid caching
|
||||
api.dev.scripting.Build(scriptName);
|
||||
} catch (ex) {
|
||||
resp = {
|
||||
result: Response.ERROR_UNKNOWN,
|
||||
error: toJSON(ex)
|
||||
};
|
||||
}
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
|
||||
me.scheduleBackup = function scheduleBackup() {
|
||||
var quartz = CronToQuartzConverter.convert(config.cronTime);
|
||||
|
||||
for (var i = quartz.length; i--;) {
|
||||
var resp = api.utils.scheduler.CreateEnvTask({
|
||||
appid: appid,
|
||||
envName: config.envName,
|
||||
session: session,
|
||||
script: config.scriptName,
|
||||
trigger: "cron:" + quartz[i],
|
||||
params: {
|
||||
task: 1,
|
||||
action: "backup"
|
||||
}
|
||||
});
|
||||
|
||||
if (resp.result !== 0) return resp;
|
||||
}
|
||||
|
||||
return {
|
||||
result: 0
|
||||
};
|
||||
};
|
||||
|
||||
me.clearScheduledBackups = function clearScheduledBackups() {
|
||||
var envAppid = config.envAppid,
|
||||
resp = api.utils.scheduler.GetTasks(envAppid, session);
|
||||
|
||||
if (resp.result != 0) return resp;
|
||||
|
||||
var tasks = resp.objects;
|
||||
|
||||
for (var i = tasks.length; i--;) {
|
||||
if (tasks[i].script == config.scriptName) {
|
||||
resp = api.utils.scheduler.RemoveTask(envAppid, session, tasks[i].id);
|
||||
|
||||
if (resp.result != 0) return resp;
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
me.getFileUrl = function (filePath) {
|
||||
return config.baseUrl + "/" + filePath + "?_r=" + Math.random();
|
||||
};
|
||||
|
||||
me.getScriptUrl = function (scriptName) {
|
||||
return me.getFileUrl("scripts/" + scriptName);
|
||||
};
|
||||
|
||||
me.cmd = function cmd(commands, values, sep) {
|
||||
return nodeManager.cmd(commands, values, sep, true);
|
||||
};
|
||||
|
||||
me.replaceText = function (text, values) {
|
||||
return new StrSubstitutor(values, "${", "}").replace(text);
|
||||
};
|
||||
|
||||
me.exec = function (methods, oScope, bBreakOnError) {
|
||||
var scope, resp, fn;
|
||||
|
||||
if (!methods.push) {
|
||||
methods = [Array.prototype.slice.call(arguments)];
|
||||
onFail = null;
|
||||
bBreakOnError = true;
|
||||
}
|
||||
|
||||
for (var i = 0, n = methods.length; i < n; i++) {
|
||||
if (!methods[i].push) {
|
||||
methods[i] = [methods[i]];
|
||||
}
|
||||
|
||||
fn = methods[i][0];
|
||||
methods[i].shift();
|
||||
|
||||
log(fn.name + (methods[i].length > 0 ? ": " + methods[i] : ""));
|
||||
scope = oScope || (methods[methods.length - 1] || {}).scope || this;
|
||||
resp = fn.apply(scope, methods[i]);
|
||||
|
||||
log(fn.name + ".response: " + resp);
|
||||
|
||||
if (resp.result != 0) {
|
||||
resp.method = fn.name;
|
||||
resp.type = "error";
|
||||
|
||||
if (resp.error) {
|
||||
resp.message = resp.error;
|
||||
}
|
||||
|
||||
if (bBreakOnError !== false) break;
|
||||
}
|
||||
}
|
||||
|
||||
return resp;
|
||||
};
|
||||
|
||||
var CronToQuartzConverter = use("https://raw.githubusercontent.com/jelastic-jps/common/main/CronToQuartzConverter");
|
||||
|
||||
function use(script) {
|
||||
var Transport = com.hivext.api.core.utils.Transport,
|
||||
body = new Transport().get(script + "?_r=" + Math.random());
|
||||
|
||||
return new(new Function("return " + body)())(session);
|
||||
}
|
||||
|
||||
function NodeManager(envName, baseDir, logPath) {
|
||||
var ENV_STATUS_TYPE_RUNNING = 1,
|
||||
me = this,
|
||||
envInfo;
|
||||
|
||||
me.isEnvRunning = function () {
|
||||
var resp = me.getEnvInfo();
|
||||
|
||||
if (resp.result != 0) {
|
||||
throw new Error("can't get environment info: " + toJSON(resp));
|
||||
}
|
||||
|
||||
return resp.env.status == ENV_STATUS_TYPE_RUNNING;
|
||||
};
|
||||
|
||||
me.getEnvInfo = function () {
|
||||
var resp;
|
||||
|
||||
if (!envInfo) {
|
||||
resp = api.env.control.GetEnvInfo(envName, session);
|
||||
if (resp.result != 0) return resp;
|
||||
|
||||
envInfo = resp;
|
||||
}
|
||||
|
||||
return envInfo;
|
||||
};
|
||||
|
||||
me.cmd = function (cmd, values, sep, disableLogging) {
|
||||
var resp, command;
|
||||
|
||||
values = values || {};
|
||||
values.log = values.log || logPath;
|
||||
cmd = cmd.join ? cmd.join(sep || " && ") : cmd;
|
||||
|
||||
command = _(cmd, values);
|
||||
|
||||
if (!disableLogging) {
|
||||
log("cmd: " + command);
|
||||
}
|
||||
|
||||
// Since we're removing specific node handling, we'll execute commands at the environment level
|
||||
resp = api.env.control.ExecCmdByGroup(envName, session, "cp", toJSON([{ command: command }]), true, false, "root");
|
||||
|
||||
if (resp.result != 0) {
|
||||
var title = "Backup failed for " + envName,
|
||||
text = "Backup failed for the environment " + envName + " of " + user.email + " with error message " + resp.responses[0].errOut;
|
||||
try {
|
||||
api.message.email.Send(appid, signature, null, user.email, user.email, title, text);
|
||||
} catch (ex) {
|
||||
emailResp = error(Response.ERROR_UNKNOWN, toJSON(ex));
|
||||
}
|
||||
}
|
||||
return resp;
|
||||
};
|
||||
}
|
||||
|
||||
function log(message) {
|
||||
Logger.debug(message);
|
||||
return api.marketplace.console.WriteLog(appid, session, message);
|
||||
}
|
||||
|
||||
function _(str, values) {
|
||||
return new StrSubstitutor(values || {}, "%(", ")").replace(str);
|
||||
}
|
||||
|
||||
me.backup_wp_core = function () {
|
||||
var backupCallParams = {
|
||||
envName: config.envName,
|
||||
backupPath: "/mnt/backup", // Direct path to mounted backup storage
|
||||
baseUrl: config.baseUrl,
|
||||
backupType: "wp_core",
|
||||
backupLogFile: "/var/log/backup_addon.log",
|
||||
session: session,
|
||||
email: user.email
|
||||
};
|
||||
|
||||
return me.exec([
|
||||
[me.checkEnvStatus],
|
||||
[me.checkCurrentlyRunningBackup],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh backup_wp_core %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(session) %(email)'
|
||||
], backupCallParams]
|
||||
]);
|
||||
};
|
||||
me.backup_uploads = function () {
|
||||
var backupCallParams = {
|
||||
envName: config.envName,
|
||||
backupPath: "/mnt/backup", // Direct path to mounted backup storage
|
||||
baseUrl: config.baseUrl,
|
||||
backupType: "wp_core",
|
||||
backupLogFile: "/var/log/backup_addon.log",
|
||||
session: session,
|
||||
email: user.email
|
||||
};
|
||||
|
||||
return me.exec([
|
||||
[me.checkEnvStatus],
|
||||
[me.checkCurrentlyRunningBackup],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh backup_uploads %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(session) %(email)'
|
||||
], backupCallParams]
|
||||
]);
|
||||
};
|
||||
me.backup_database = function () {
|
||||
var backupCallParams = {
|
||||
envName: config.envName,
|
||||
backupPath: "/mnt/backup", // Direct path to mounted backup storage
|
||||
baseUrl: config.baseUrl,
|
||||
backupType: "wp_core",
|
||||
backupLogFile: "/var/log/backup_addon.log",
|
||||
session: session,
|
||||
email: user.email
|
||||
};
|
||||
|
||||
return me.exec([
|
||||
[me.checkEnvStatus],
|
||||
[me.checkCurrentlyRunningBackup],
|
||||
[me.cmd, [
|
||||
'bash /root/%(envName)_backup-logic.sh backup_database %(baseUrl) %(backupType) %(backupPath) %(backupLogFile) %(envName) %(session) %(email)'
|
||||
], backupCallParams]
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue