2024-03-13 13:25:45 +00:00
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 ,
2025-07-17 16:07:04 +00:00
backupPath : "/data/backups" // Direct path to mounted backup storage
2024-03-13 13:25:45 +00:00
} ;
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 ,
2025-07-17 16:07:04 +00:00
backupPath : "/data/backups" , // Direct path to mounted backup storage
2024-03-13 13:25:45 +00:00
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 ,
2025-07-17 16:07:04 +00:00
backupPath : "/data/backups" , // Direct path to mounted backup storage
2024-03-13 13:25:45 +00:00
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 ,
2025-07-17 16:07:04 +00:00
backupPath : "/data/backups" , // Direct path to mounted backup storage
2024-03-13 13:25:45 +00:00
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 ]
] ) ;
} ;
}