Compare commits

...

2 Commits

Author SHA1 Message Date
Anthony 71b172aeac Revised backup logic 2024-03-13 21:25:45 +08:00
Anthony d123a32517 Replaced file 2024-03-12 23:49:47 +08:00
7 changed files with 705 additions and 129 deletions

View File

@ -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,7 +59,9 @@ actions:
- stopEvent:
type: warning
message: Deployed application is not supported by Backup add-on.
backupnow:
message: BACKUP ALL DONE
checkAddons:
- script: |-
var onAfterReturn = { setGlobals: {} },
@ -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,13 +115,10 @@ 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: |-
var resp = api.dev.scripting.Eval(appid, session, '${env.envName}-${globals.scriptSufix}', {action:"${this}"});
@ -133,18 +145,4 @@ actions:
echo "${settings.backupedEnvName}" > /root/.backupedenv
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
- callScript: restore

View File

@ -0,0 +1,8 @@
/var/log/backup_addon.log {
weekly
rotate 52
missingok
notifempty
compress
copytruncate
}

View File

@ -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 $?

View File

@ -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(

View File

@ -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

View File

@ -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;
};
}

View File

@ -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]
]);
};
}