Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66b02b58b6 | |||
| 4428c3d7d8 | |||
| 2d4b9786fa | |||
| d2d9c4be6f | |||
| a9d6ac29f1 | |||
| 4d50bd5c78 | |||
| fdd651b9cc |
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
<% } %>
|
||||
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user