ci: scripts to redeploy in developments #1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
jpsType: update | |
jpsVersion: '1.7.4' | |
name: Scripts/DailyTask | |
id: daily-task | |
description: | |
short: Sysadmin Actions for jelastic account | |
categories: | |
- apps/platforms | |
ssl: true | |
ha: false | |
settings: | |
fields: | |
- name: RESTIC_REPOSITORY | |
type: string | |
caption: Host+bucket+region for the S3 | |
- name: AWS_ACCESS_KEY_ID | |
type: string | |
caption: Key ID for S3 | |
- name: AWS_SECRET_ACCESS_KEY | |
type: string | |
caption: Secret for S3 | |
- name: RESTIC_PASSWORD | |
type: string | |
caption: Key used for restic encryption | |
- name: AWS_DEFAULT_REGION | |
type: string | |
caption: Default Region | |
default: 'ch-dk-2' | |
- name: TAG | |
type: string | |
caption: Tag the backup | |
default: 'daily' | |
onInstall: | |
- type: "javascript" | |
script: | |
- 'var resp = jelastic.environment.control.GetEnvs(appid, session);' | |
- 'if(resp.result != 0) return {"result": 1, "message": "ERROR"};' | |
- 'var nodes = (resp.infos || []).filter(function(env) { | |
return (env.env.status || 0) === 1; | |
}).reduce(function(acc, curr) { | |
acc[curr.env.envName] = { | |
raw: curr, | |
nodes: curr.nodes.filter(function(nd) { | |
if(nd.nodeGroup === "cp" && curr.nodes.find(function(nd2) { return nd2.nodeGroup === "storage"; })) { | |
return false; | |
} | |
return (typeof nd.customitem !== "undefined") && | |
(typeof nd.customitem.dockerVolumes !== "undefined") && | |
(nd.customitem.dockerVolumes.length > 0) && | |
["cp", "bl", "sqldb", "storage"].includes(nd.nodeGroup); | |
}).map(function(nd) { | |
return { | |
nodeGroup: nd.nodeGroup, | |
volumes: nd.customitem.dockerVolumes, | |
id: nd.id, | |
ip: nd.intIP, | |
port: nd.port | |
}; | |
}) | |
}; | |
return acc; | |
}, {})' | |
- 'var result = Object.keys(nodes).map(function(envName) { | |
var nd = nodes[envName]; | |
nd.envName = envName; | |
return nd; | |
})' | |
- 'return {"result": 0,"environments": JSON.stringify(result)}' | |
- setGlobals: | |
- ENVIRONMENTS: ${response.environments} | |
- type: javascript | |
environments: ${globals.ENVIRONMENTS} | |
script: | |
- 'var installRestic = function(envName, nodeGroups){ | |
var results = nodeGroups.map(function(nodeGroup) { | |
var resp = jelastic.environment.control.ExecCmdByGroup( | |
envName, | |
session, | |
nodeGroup, | |
JSON.stringify([ | |
{ | |
"command": "which restic >/dev/null || { which dnf >/dev/null && { dnf install -y epel-release; dnf install -y restic; } || { which apk >/dev/null && apk add restic; } || { which yum >/dev/null && { yum-config-manager --add-repo https://copr.fedorainfracloud.org/coprs/copart/restic/repo/epel-7/copart-restic-epel-7.repo; yum-config-manager --enable copr:copr.fedorainfracloud.org:copart:restic; yum -y install restic; yum-config-manager --disable copr:copr.fedorainfracloud.org:copart:restic; } ; } ; }" | |
} | |
]) | |
); | |
return resp.result === 0; | |
}); | |
return results.filter(Boolean).length === results.length; | |
}' | |
- 'var environments = JSON.parse(getParam("environments"))' | |
- 'var failedInstall = environments.filter(function(environment) { | |
var nodes = environment.nodes; | |
var nodeGroups = nodes.map(function(nd) { return nd.nodeGroup }); | |
return !installRestic(environment.envName, nodeGroups) | |
})' | |
- 'var outputMessage = failedInstall.length > 0 ? | |
( | |
"Fail to install Restic. Check environments " + failedInstall.map(function(environment) { | |
return "`" + environment.envName + "`"; | |
}).join(", ") | |
) : "OK"' | |
- 'return {result: 0, outputMessage: outputMessage }' | |
- setGlobals: | |
- OUTPUT_RESTIC_INSTALL: ${response.outputMessage} | |
- type: javascript | |
environments: ${globals.ENVIRONMENTS} | |
script: | |
- 'var backupContainer = function(envName, nodeGroup, directories, topology){ | |
var tag = "${settings.TAG}"; | |
var repo = "s3:${settings.RESTIC_REPOSITORY}/" + envName + "-" + nodeGroup; | |
var keyId = "${settings.AWS_ACCESS_KEY_ID}"; | |
var keySecret = "${settings.AWS_SECRET_ACCESS_KEY}"; | |
var resticPassword = "${settings.RESTIC_PASSWORD}"; | |
var defaultRegion = "${settings.AWS_DEFAULT_REGION}"; | |
if(topology) { | |
jelastic.environment.control.ExecCmdByGroup( | |
envName, | |
session, | |
nodeGroup, | |
JSON.stringify([ | |
{ | |
"command": "mkdir -p /tmp/topology", | |
"params": "" | |
} | |
]) | |
); | |
directories.push("/tmp/topology"); | |
jelastic.environment.file.write( | |
envName, | |
session, | |
"/tmp/topology/topology.json", | |
JSON.stringify(topology), | |
undefined, | |
nodeGroup | |
); | |
} | |
var resp = jelastic.environment.control.ExecCmdByGroup( | |
envName, | |
session, | |
nodeGroup, | |
JSON.stringify([ | |
{ | |
"command": "export RESTIC_REPOSITORY=\""+repo+"\"", | |
"params": "" | |
}, | |
{ | |
"command": "export AWS_ACCESS_KEY_ID=\""+keyId+"\"", | |
"params": "" | |
}, | |
{ | |
"command": "export AWS_SECRET_ACCESS_KEY=\""+keySecret+"\"", | |
"params": "" | |
}, | |
{ | |
"command": "export RESTIC_PASSWORD=\""+resticPassword+"\"", | |
"params": "" | |
}, | |
{ | |
"command": "export AWS_DEFAULT_REGION=\""+defaultRegion+"\"", | |
"params": "" | |
}, | |
{ | |
"command": "if ! restic snapshots >/dev/null 2>&1; then restic init; fi", | |
"params": "" | |
}, | |
{ | |
"command": "restic backup --tag "+ tag +" " + directories.filter(function(dir) { | |
return !dir.endsWith("vendor") && !dir.endsWith("node_modules"); | |
}).join(" "), | |
"params": "" | |
} | |
]) | |
); | |
return resp.result === 0; | |
}' | |
- 'var backupDatabase = function(envName){ | |
var resp = jelastic.environment.control.ExecCmdByGroup( | |
envName, | |
session, | |
"sqldb", | |
JSON.stringify([ | |
{ | |
"command": "mkdir -p /root/dump", | |
"params": "" | |
}, | |
{ | |
"command": "cd /root/dump", | |
"params": "" | |
}, | |
{ | |
"command": "databases=$(psql --username $POSTGRES_USER postgres -qAt -c \"SELECT datname FROM pg_database WHERE NOT datistemplate\")", | |
"params": "" | |
}, | |
{ | |
"command": "for db in $databases; do pg_dump --username $POSTGRES_USER -Fp $db > /root/dump/$db.sql; done", | |
"params": "" | |
} | |
]) | |
); | |
return resp.result == 0; | |
}' | |
- 'var environments = JSON.parse(getParam("environments"))' | |
- 'var backups = environments.map(function(environment) { | |
var nodes = environment.nodes; | |
var raw = environment.raw; | |
var envName = environment.envName; | |
var nodeGroups = nodes.map(function(nd) { return nd.nodeGroup }); | |
var backupProcess = []; | |
var hasStorage = nodeGroups.includes("storage"); | |
if(nodeGroups.includes("sqldb")) { | |
var matchNode = nodes.find(function(nd) { return nd.nodeGroup == "sqldb";}); | |
backupProcess.push(backupContainer(envName, "sqldb", ["/root/dump"], undefined)); | |
} | |
if(nodeGroups.includes("cp")) { | |
var matchNode = nodes.find(function(nd) { return nd.nodeGroup == "cp";}); | |
backupProcess.push(backupContainer(envName, "cp", matchNode.volumes, hasStorage ? undefined : raw)); | |
} | |
if(hasStorage) { | |
var matchNode = nodes.find(function(nd) { return nd.nodeGroup == "storage";}); | |
backupProcess.push(backupContainer(envName, "storage", matchNode.volumes, raw)); | |
} | |
if(nodeGroups.includes("bl")) { | |
var matchNode = nodes.find(function(nd) { return nd.nodeGroup == "bl";}); | |
backupProcess.push(backupContainer(envName, "bl", matchNode.volumes, undefined)); | |
} | |
return [ | |
envName, | |
backupProcess.length > 0 && backupProcess.filter(Boolean).length === backupProcess.length | |
]; | |
})' | |
- 'var outputMessage = backups.map(function(backupInfo) { | |
return " * " + backupInfo[0] + ": " + (backupInfo[1] ? "✅" : "⚠️") | |
}).join("\n")' | |
- 'return {"result": 0, outputMessage: outputMessage}' | |
- setGlobals: | |
- OUTPUT_BACKUP: ${response.outputMessage} | |
- type: javascript | |
environments: ${globals.ENVIRONMENTS} | |
script: | |
- 'var fetchBilling = function(targetAppId, envName) { | |
var yesterdayStart = new Date(); | |
yesterdayStart.setDate(yesterdayStart.getDate() - 1); | |
yesterdayStart.setUTCHours(0,0,0,0); | |
yesterdayStart = yesterdayStart.toISOString(); | |
var yesterdayEnd = new Date(); | |
yesterdayEnd.setDate(yesterdayEnd.getDate() - 1); | |
yesterdayEnd.setUTCHours(23,59,59,999); | |
yesterdayEnd = yesterdayEnd.toISOString(); | |
var billingHistory = jelastic.billing.account.GetExtendedAccountBillingHistoryByPeriod( | |
appid, | |
session, | |
yesterdayStart, | |
yesterdayEnd, | |
targetAppId | |
); | |
if(!billingHistory || billingHistory.result !== 0 || billingHistory.object.envs.length === 0) { | |
return null; | |
} | |
var envBillingHistory = billingHistory.object.envs[0][envName]; | |
var cloudlets = (envBillingHistory.cloudlets.flexible.cost || 0) + (envBillingHistory.cloudlets.fixed.cost || 0); | |
var storage = envBillingHistory.storage.total.cost || 0; | |
var ips = envBillingHistory.ips.cost || 0; | |
var ssl = envBillingHistory.ssl.cost || 0; | |
return { | |
"total": (cloudlets + storage + ips + ssl).toFixed(2), | |
"ips": ips.toFixed(2), | |
"cloudlets": cloudlets.toFixed(2), | |
"storage": storage.toFixed(2), | |
"ssl": ssl.toFixed(2) | |
}; | |
}' | |
- 'var environments = JSON.parse(getParam("environments"))' | |
- 'var billings = environments.map(function(environment) { | |
var billingHistory = fetchBilling(environment.raw.env.appid, envName); | |
return [ | |
"Yesterday Billing " + (billingHistory ? billingHistory.total + "CHF" : "unknown"), | |
"_Yesterday Billing Details_", | |
(billingHistory ? Object.keys(billingHistory).filter(function(k) { return k !== "total"}).map(function(k) { | |
var value = billingHistory[k]; | |
return " * __"+ k + "__: " + (value === "0" ? "free" : value + "CHF") | |
}).join("\n") : "unknown") | |
].join("\n\n"); | |
}' | |
- 'var outputMessage = billings.join("\n")' | |
- 'return {"result": 0, outputMessage: outputMessage}' | |
- setGlobals: | |
- OUTPUT_BILLING: ${response.outputMessage} | |
- return: | |
type: success | |
message: | | |
# Jelastic Daily Review | |
## Backup Status | |
**▪️ Backup are installed** | |
${globals.OUTPUT_RESTIC_INSTALL} | |
**▪️ Backup are done** | |
${globals.OUTPUT_BACKUP} | |
## Billing status | |
${globals.OUTPUT_BILLING} | |