Compare commits

..

7 Commits

Author SHA1 Message Date
Girish Ramakrishnan 66b02b58b6 make notifications.alert take a title
the title is better when it's a bit more dynamic
2019-03-08 16:59:48 -08:00
Girish Ramakrishnan 4428c3d7d8 Move docker config file generation to installer logic
the new version of docker does not support devicemapper on ubuntu 16.
so, we have to first enable overlay2 and then install the latest docker
2019-03-08 16:41:39 -08:00
Girish Ramakrishnan 2d4b9786fa box update is now an alert notification 2019-03-07 14:40:46 -08:00
Girish Ramakrishnan d2d9c4be6f update notification 2019-03-07 14:27:43 -08:00
Girish Ramakrishnan a9d6ac29f1 make funcs take proper callbacks 2019-03-07 14:27:23 -08:00
Girish Ramakrishnan 4d50bd5c78 3.5.4 changes 2019-03-07 13:40:20 -08:00
Girish Ramakrishnan fdd651b9cc Only append markdownMessage is not-empty 2019-03-07 11:50:49 -08:00
11 changed files with 50 additions and 155 deletions
+13
View File
@@ -1550,3 +1550,16 @@
* Add notification for cert renewal and backup failures
* Fix issue where mail container was not updated with the latest certificate
[3.5.4]
* Make reboot required check server side
* Update node to 10.15.1
* Enable gzip compression for large objects
* Update docker to 18.09
* Add a way to lock specific settings
* Add UI to copy app's backup id
* Block platform updates based on app manifest constraints
* Make crash logs viewable via the dashboard
* Fix issue where uploading of filenames with brackets and plus was not working
* Add notification for cert renewal and backup failures
* Fix issue where mail container was not updated with the latest certificate
+1 -1
View File
@@ -66,7 +66,7 @@ apt-get install -y python # Install python which is required for npm rebuild
# https://docs.docker.com/engine/installation/linux/ubuntulinux/
echo "==> Installing Docker"
# create systemd drop-in file
# create systemd drop-in file. if you channge options here, be sure to fixup installer.sh as well
mkdir -p /etc/systemd/system/docker.service.d
echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=overlay2" > /etc/systemd/system/docker.service.d/cloudron.conf
+13
View File
@@ -25,6 +25,19 @@ readonly ubuntu_codename=$(lsb_release -cs)
readonly is_update=$(systemctl is-active box && echo "yes" || echo "no")
echo "==> installer: updating docker"
# verify that existing docker installation uses overlay2. devicemapper does not works with ubuntu 16/docker 18.09
# if you change the drop-in file below be sure to change initializeUbuntuBaseImage script as well
if ! grep -q "storage-driver=overlay2" /etc/systemd/system/docker.service.d/cloudron.conf; then
echo "==> installer: restarting docker with overlay2 backend"
echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=overlay2" > /etc/systemd/system/docker.service.d/cloudron.conf
systemctl daemon-reload
systemctl restart docker
# trigger a re-configure after the box starts up, so that all images are repulled with the new graphdriver
sed -e "s/48.12.1/48.12.0/" -i /home/yellowtent/platformdata/INFRA_VERSION
fi
if [[ $(docker version --format {{.Client.Version}}) != "18.09.2" ]]; then
# there are 3 packages for docker - containerd, CLI and the daemon
$curl -sL "https://download.docker.com/linux/ubuntu/dists/${ubuntu_codename}/pool/stable/amd64/containerd.io_1.2.2-3_amd64.deb" -o /tmp/containerd.deb
-16
View File
@@ -26,22 +26,6 @@ systemctl enable apparmor
systemctl restart apparmor
usermod ${USER} -a -G docker
# preserve the existing storage driver (user might be using overlay2)
storage_driver=$(docker info | grep "Storage Driver" | sed 's/.*: //')
[[ -n "${storage_driver}" ]] || storage_driver="overlay2" # if the above command fails
temp_file=$(mktemp)
# create systemd drop-in. some apps do not work with aufs
echo -e "[Service]\nExecStart=\nExecStart=/usr/bin/dockerd -H fd:// --log-driver=journald --exec-opt native.cgroupdriver=cgroupfs --storage-driver=${storage_driver}" > "${temp_file}"
systemctl enable docker
# restart docker if options changed
if [[ ! -f /etc/systemd/system/docker.service.d/cloudron.conf ]] || ! diff -q /etc/systemd/system/docker.service.d/cloudron.conf "${temp_file}" >/dev/null; then
mkdir -p /etc/systemd/system/docker.service.d
mv "${temp_file}" /etc/systemd/system/docker.service.d/cloudron.conf
systemctl daemon-reload
systemctl restart docker
fi
docker network create --subnet=172.18.0.0/16 cloudron || true
mkdir -p "${BOX_DATA_DIR}"
+4 -4
View File
@@ -208,7 +208,7 @@ function checkBackupConfiguration(callback) {
backups.checkConfiguration(function (error, message) {
if (error) return callback(error);
notifications.alert(notifications.ALERT_BACKUP_CONFIG, message, callback);
notifications.alert(notifications.ALERT_BACKUP_CONFIG, 'Backup configuration is unsafe', message, callback);
});
}
@@ -243,7 +243,7 @@ function checkDiskSpace(callback) {
debug('Disk space checked. ok: %s', !oos);
notifications.alert(notifications.ALERT_DISK_SPACE, oos ? JSON.stringify(entries, null, 4) : '', callback);
notifications.alert(notifications.ALERT_DISK_SPACE, 'Server is running out of disk space', oos ? JSON.stringify(entries, null, 4) : '', callback);
}).catch(function (error) {
if (error) console.error(error);
callback();
@@ -259,7 +259,7 @@ function checkMailStatus(callback) {
mail.checkConfiguration(function (error, message) {
if (error) return callback(error);
notifications.alert(notifications.ALERT_MAIL_STATUS, message, callback);
notifications.alert(notifications.ALERT_MAIL_STATUS, 'Email is not configured properly', message, callback);
});
}
@@ -271,7 +271,7 @@ function checkRebootRequired(callback) {
isRebootRequired(function (error, rebootRequired) {
if (error) return callback(error);
notifications.alert(notifications.ALERT_REBOOT, rebootRequired ? 'To finish security updates, a [reboot](/#/system) is necessary.' : '', callback);
notifications.alert(notifications.ALERT_REBOOT, 'Reboot Required', rebootRequired ? 'To finish security updates, a [reboot](/#/system) is necessary.' : '', callback);
});
}
+2 -2
View File
@@ -140,7 +140,7 @@ function recreateJobs(tz) {
if (gJobs.boxUpdateCheckerJob) gJobs.boxUpdateCheckerJob.stop();
gJobs.boxUpdateCheckerJob = new CronJob({
cronTime: '00 ' + randomMinute + ' * * * *', // once an hour
onTick: updateChecker.checkBoxUpdates,
onTick: () => updateChecker.checkBoxUpdates(NOOP_CALLBACK),
start: true,
timeZone: tz
});
@@ -148,7 +148,7 @@ function recreateJobs(tz) {
if (gJobs.appUpdateChecker) gJobs.appUpdateChecker.stop();
gJobs.appUpdateChecker = new CronJob({
cronTime: '00 ' + randomMinute + ' * * * *', // once an hour
onTick: updateChecker.checkAppUpdates,
onTick: () => updateChecker.checkAppUpdates(NOOP_CALLBACK),
start: true,
timeZone: tz
});
+2 -2
View File
@@ -545,9 +545,9 @@ function checkConfiguration(callback) {
markdownMessage += '\n\n';
});
markdownMessage += 'Email Status is checked every 30 minutes\n See the [troubleshooting docs](https://cloudron.io/documentation/troubleshooting/#mail-dns) for more information.\n';
if (markdownMessage) markdownMessage += 'Email Status is checked every 30 minutes\n See the [troubleshooting docs](https://cloudron.io/documentation/troubleshooting/#mail-dns) for more information.\n';
callback(null, markdownMessage);
callback(null, markdownMessage); // empty message means all status checks succeeded
});
});
}
@@ -1,56 +0,0 @@
<%if (format === 'text') { %>
Dear <%= cloudronName %> Admin,
Version <%= newBoxVersion %> is now available!
Changelog:
<% for (var i = 0; i < changelog.length; i++) { %>
* <%- changelog[i] %>
<% } %>
<% if (!hasSubscription) { -%>
*Keep your Cloudron automatically up-to-date and secure by upgrading to a paid plan at* <%= webadminUrl %>/#/settings
<% } -%>
Powered by https://cloudron.io
Sent at: <%= new Date().toUTCString() %>
<% } else { %>
<center>
<img src="<%= cloudronAvatarUrl %>" width="128px" height="128px"/>
<h3>Dear <%= cloudronName %> Admin,</h3>
<div style="width: 650px; text-align: left;">
<p>
Version <b><%= newBoxVersion %></b> is now available!
</p>
<h5>Changelog:</h5>
<ul>
<% for (var i = 0; i < changelogHTML.length; i++) { %>
<li><%- changelogHTML[i] %></li>
<% } %>
</ul>
<br/>
<% if (!hasSubscription) { %>
<p>Keep your Cloudron automatically up-to-date and secure by upgrading to a <a href="<%= webadminUrl %>/#/settings">paid plan</a>.</p>
<% } %>
<br/>
</div>
<div style="font-size: 10px; color: #333333; background: #ffffff;">
Powered by <a href="https://cloudron.io">Cloudron</a>.
</div>
</center>
<% } %>
-39
View File
@@ -5,7 +5,6 @@ exports = module.exports = {
userRemoved: userRemoved,
adminChanged: adminChanged,
passwordReset: passwordReset,
boxUpdateAvailable: boxUpdateAvailable,
appUpdateAvailable: appUpdateAvailable,
sendDigest: sendDigest,
@@ -341,44 +340,6 @@ function appDied(mailTo, app) {
});
}
function boxUpdateAvailable(hasSubscription, newBoxVersion, changelog) {
assert.strictEqual(typeof hasSubscription, 'boolean');
assert.strictEqual(typeof newBoxVersion, 'string');
assert(util.isArray(changelog));
getMailConfig(function (error, mailConfig) {
if (error) return debug('Error getting mail details:', error);
var converter = new showdown.Converter();
var templateData = {
webadminUrl: config.adminOrigin(),
newBoxVersion: newBoxVersion,
hasSubscription: hasSubscription,
changelog: changelog,
changelogHTML: changelog.map(function (e) { return converter.makeHtml(e); }),
cloudronName: mailConfig.cloudronName,
cloudronAvatarUrl: config.adminOrigin() + '/api/v1/cloudron/avatar'
};
var templateDataText = JSON.parse(JSON.stringify(templateData));
templateDataText.format = 'text';
var templateDataHTML = JSON.parse(JSON.stringify(templateData));
templateDataHTML.format = 'html';
var mailOptions = {
from: mailConfig.notificationFrom,
to: mailConfig.adminEmails.join(', '),
subject: util.format('%s has a new update available', mailConfig.cloudronName),
text: render('box_update_available.ejs', templateDataText),
html: render('box_update_available.ejs', templateDataHTML)
};
enqueue(mailOptions);
});
}
function appUpdateAvailable(app, hasSubscription, info) {
assert.strictEqual(typeof app, 'object');
assert.strictEqual(typeof hasSubscription, 'boolean');
+3 -12
View File
@@ -14,6 +14,7 @@ exports = module.exports = {
ALERT_DISK_SPACE: 'diskSpace',
ALERT_MAIL_STATUS: 'mailStatus',
ALERT_REBOOT: 'reboot',
ALERT_BOX_UPDATE: 'boxUpdate',
alert: alert,
@@ -35,14 +36,6 @@ let assert = require('assert'),
users = require('./users.js'),
util = require('util');
// These titles are matched for upsert
const ALERT_TITLES = {
backupConfig: 'Backup configuration is unsafe',
diskSpace: 'Out of Disk Space',
mailStatus: 'Email is not configured properly',
reboot: 'Reboot Required'
};
function NotificationsError(reason, errorOrMessage) {
assert.strictEqual(typeof reason, 'string');
assert(errorOrMessage instanceof Error || typeof errorOrMessage === 'string' || typeof errorOrMessage === 'undefined');
@@ -333,14 +326,12 @@ function upsert(userId, eventId, title, message, callback) {
});
}
function alert(id, message, callback) {
function alert(id, title, message, callback) {
assert.strictEqual(typeof id, 'string');
assert.strictEqual(typeof title, 'string');
assert.strictEqual(typeof message, 'string');
assert.strictEqual(typeof callback, 'function');
const title = ALERT_TITLES[id];
if (!title) return callback();
debug(`alert: id=${id} title=${title} message=${message}`);
actionForAllAdmins([], function (admin, callback) {
+12 -23
View File
@@ -13,10 +13,12 @@ exports = module.exports = {
var apps = require('./apps.js'),
appstore = require('./appstore.js'),
assert = require('assert'),
async = require('async'),
constants = require('./constants.js'),
debug = require('debug')('box:updatechecker'),
mailer = require('./mailer.js'),
notifications = require('./notifications.js'),
paths = require('./paths.js'),
safe = require('safetydance'),
settings = require('./settings.js');
@@ -24,8 +26,6 @@ var apps = require('./apps.js'),
var gAppUpdateInfo = { }, // id -> update info { creationDate, manifest }
gBoxUpdateInfo = null; // { version, changelog, upgrade, sourceTarballUrl }
var NOOP_CALLBACK = function (error) { if (error) debug(error); };
function loadState() {
var state = safe.JSON.parse(safe.fs.readFileSync(paths.UPDATE_CHECKER_FILE, 'utf8'));
return state || { };
@@ -62,7 +62,7 @@ function resetAppUpdateInfo(appId) {
}
function checkAppUpdates(callback) {
callback = callback || NOOP_CALLBACK; // null when called from a timer task
assert.strictEqual(typeof callback, 'function');
debug('Checking App Updates');
@@ -133,7 +133,7 @@ function checkAppUpdates(callback) {
}
function checkBoxUpdates(callback) {
callback = callback || NOOP_CALLBACK; // null when called from a timer task
assert.strictEqual(typeof callback, 'function');
debug('Checking Box Updates');
@@ -152,28 +152,17 @@ function checkBoxUpdates(callback) {
return callback();
}
appstore.getSubscription(function (error, result) {
const changelog = updateInfo.changelog.map((m) => `* ${m}\n`).join('');
const message = `Changelog:\n${changelog}\n\nClick [here](/#/settings) to update.\n\n`;
notifications.alert(notifications.ALERT_BOX_UPDATE, `Cloudron v${updateInfo.version} is available`, message, function (error) {
if (error) return callback(error);
function done() {
state.box = updateInfo.version;
saveState(state);
callback();
}
state.box = updateInfo.version;
saveState(state);
// always send notifications if user is on the free plan
if (appstore.isFreePlan(result)) {
mailer.boxUpdateAvailable(false /* hasSubscription */, updateInfo.version, updateInfo.changelog);
return done();
}
// only send notifications if update pattern is 'never'
settings.getBoxAutoupdatePattern(function (error, result) {
if (error) debug(error);
else if (result === constants.AUTOUPDATE_PATTERN_NEVER) mailer.boxUpdateAvailable(true /* hasSubscription */, updateInfo.version, updateInfo.changelog);
done();
});
callback();
});
});
}