From 9854a4b965d777e190ce1a7d68d9acd5ab8bd4e3 Mon Sep 17 00:00:00 2001 From: Sylvain Date: Tue, 26 Feb 2019 15:18:19 +0100 Subject: [PATCH] prevent memory saturation with periodic checks --- CHANGELOG.md | 1 + Gemfile | 3 +++ Gemfile.lock | 3 +++ app/assets/stylesheets/modules/invoice.scss | 3 ++- app/models/notification_type.rb | 1 + ...notify_admin_free_disk_space.json.jbuilder | 3 +++ .../notify_admin_free_disk_space.html.erb | 4 ++++ app/workers/free_disk_space_worker.rb | 23 +++++++++++++++++++ config/application.yml.default | 1 + config/locales/en.yml | 2 ++ config/locales/es.yml | 2 ++ config/locales/fr.yml | 2 ++ config/locales/mails.en.yml | 4 ++++ config/locales/mails.es.yml | 4 ++++ config/locales/mails.fr.yml | 5 ++++ config/locales/mails.pt.yml | 4 ++++ config/locales/pt.yml | 2 ++ config/schedule.yml | 5 ++++ config/secrets.yml | 6 ++++- doc/environment.md | 5 ++++ 20 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 app/views/api/notifications/_notify_admin_free_disk_space.json.jbuilder create mode 100644 app/views/notifications_mailer/notify_admin_free_disk_space.html.erb create mode 100644 app/workers/free_disk_space_worker.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 23ec96aee..0e8433910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - Fix a security issue: dependency bootstrap < 4.3.1 has an XSS vulnerability as described in [CVE-2019-8331](https://blog.getbootstrap.com/2019/02/13/bootstrap-4-3-1-and-3-4-1/) - Fixed missing translations in authentication providers form - [TODO DEPLOY] `bundle install` +- [TODO DEPLOY] add `DISK_SPACE_MB_ALERT` environment variable (see [doc/environment.md](doc/environment.md) for configuration details) ## v2.8.3 2019 January 29 diff --git a/Gemfile b/Gemfile index 964fbddea..0fe754f3e 100644 --- a/Gemfile +++ b/Gemfile @@ -148,3 +148,6 @@ gem 'axlsx_rails' gem 'rubyzip', '>= 1.2.2' gem 'rack-protection', '1.5.5' + +# get free disk space +gem 'sys-filesystem' diff --git a/Gemfile.lock b/Gemfile.lock index 22fe64557..182c10e1e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -440,6 +440,8 @@ GEM stripe (1.30.2) json (~> 1.8.1) rest-client (~> 1.4) + sys-filesystem (1.2.0) + ffi term-ansicolor (1.3.2) tins (~> 1.0) test_after_commit (1.0.0) @@ -580,6 +582,7 @@ DEPENDENCIES sinatra spring stripe (= 1.30.2) + sys-filesystem test_after_commit therubyracer (= 0.12.0) twitter diff --git a/app/assets/stylesheets/modules/invoice.scss b/app/assets/stylesheets/modules/invoice.scss index a5ca95573..4b3aebc9b 100644 --- a/app/assets/stylesheets/modules/invoice.scss +++ b/app/assets/stylesheets/modules/invoice.scss @@ -238,7 +238,8 @@ table.closings-table { tbody td.download-archive { position: absolute; right: 10px; - width: 41px; + width: 32px; + height: 32px; cursor: pointer; } diff --git a/app/models/notification_type.rb b/app/models/notification_type.rb index 11230db24..54503aa56 100644 --- a/app/models/notification_type.rb +++ b/app/models/notification_type.rb @@ -42,6 +42,7 @@ class NotificationType notify_admin_export_complete notify_member_about_coupon notify_member_reservation_reminder + notify_admin_free_disk_space ] # deprecated: # - notify_member_subscribed_plan_is_changed diff --git a/app/views/api/notifications/_notify_admin_free_disk_space.json.jbuilder b/app/views/api/notifications/_notify_admin_free_disk_space.json.jbuilder new file mode 100644 index 000000000..f729e4d01 --- /dev/null +++ b/app/views/api/notifications/_notify_admin_free_disk_space.json.jbuilder @@ -0,0 +1,3 @@ +json.title notification.notification_type +json.description t('warning_disk_space_under_threshold', THRESHOLD: notification.meta_data.threshold) +json.url notification_url(notification, format: :json) diff --git a/app/views/notifications_mailer/notify_admin_free_disk_space.html.erb b/app/views/notifications_mailer/notify_admin_free_disk_space.html.erb new file mode 100644 index 000000000..5543e94f6 --- /dev/null +++ b/app/views/notifications_mailer/notify_admin_free_disk_space.html.erb @@ -0,0 +1,4 @@ +<%= render 'notifications_mailer/shared/hello', recipient: @recipient %> + +

<%= t('.body', THRESHOLD: @notification.get_meta_data(:threshold), AVAILABLE: @notification.get_meta_data(:mb_available)) %>

+ diff --git a/app/workers/free_disk_space_worker.rb b/app/workers/free_disk_space_worker.rb new file mode 100644 index 000000000..6066e67ed --- /dev/null +++ b/app/workers/free_disk_space_worker.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +# Periodically check if the free disk space available on the host is above the configured limit, otherwise trigger an email alert +class FreeDiskSpaceWorker + include Sidekiq::Worker + + def perform + require 'sys/filesystem' + + stat = Sys::Filesystem.stat('.') + mb_available = stat.block_size * stat.blocks_available / 1024 / 1024 + + return if mb_available > Rails.application.secrets.disk_space_mb_alert + + NotificationCenter.call type: 'notify_admin_free_disk_space', + receiver: User.admins, + attached_object: Role.first, + meta_data: { + mb_available: mb_available, + threshold: Rails.application.secrets.disk_space_mb_alert + } + end +end diff --git a/config/application.yml.default b/config/application.yml.default index de0783247..db4ba61c1 100644 --- a/config/application.yml.default +++ b/config/application.yml.default @@ -61,6 +61,7 @@ OPENLAB_APP_ID: OPENLAB_BASE_URI: 'https://openprojects.fab-manager.com' LOG_LEVEL: 'debug' +DISK_SPACE_MB_ALERT: '100' ALLOWED_EXTENSIONS: pdf ai eps cad math svg stl dxf dwg obj step iges igs 3dm 3dmf doc docx png ino scad fcad skp sldprt sldasm slddrw slddrt tex latex ps ALLOWED_MIME_TYPES: application/pdf application/postscript application/illustrator image/x-eps image/svg+xml application/sla application/dxf application/acad application/dwg application/octet-stream application/step application/iges model/iges x-world/x-3dmf application/vnd.openxmlformats-officedocument.wordprocessingml.document image/png text/x-arduino text/plain application/scad application/vnd.sketchup.skp application/x-koan application/vnd-koan koan/x-skm application/vnd.koan application/x-tex application/x-latex diff --git a/config/locales/en.yml b/config/locales/en.yml index f8e2712d8..d9c88a85a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -308,6 +308,8 @@ en: notify_member_about_coupon: enjoy_a_discount_of_PERCENT_with_code_CODE: "Enjoy a discount of %{PERCENT}% with code %{CODE}" enjoy_a_discount_of_AMOUNT_with_code_CODE: "Enjoy a discount of %{AMOUNT} with code %{CODE}" + notify_admin_free_disk_space: + warning_disk_space_under_threshold: "Warning: the server's available disk space is now %{AVAILABLE} MiB" statistics: # statistics tools for admins diff --git a/config/locales/es.yml b/config/locales/es.yml index ffe6f30ff..9a910571d 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -308,6 +308,8 @@ es: notify_member_about_coupon: enjoy_a_discount_of_PERCENT_with_code_CODE: "Disfruta de un descuento de %{PERCENT}% con el código %{CODE}" enjoy_a_discount_of_AMOUNT_with_code_CODE: "Disfruta de un descuento de %{AMOUNT} con el código %{CODE}" + notify_admin_free_disk_space: + warning_disk_space_under_threshold: "Warning: the server's available disk space is now %{AVAILABLE} MiB" # missing translation statistics: # statistics tools for admins diff --git a/config/locales/fr.yml b/config/locales/fr.yml index e64845006..3df828a0e 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -308,6 +308,8 @@ fr: notify_member_about_coupon: enjoy_a_discount_of_PERCENT_with_code_CODE: "Bénéficiez d'une remise de %{PERCENT} % avec le code %{CODE}" enjoy_a_discount_of_AMOUNT_with_code_CODE: "Bénéficiez d'une remise de %{AMOUNT} avec le code %{CODE}" + notify_admin_free_disk_space: + warning_disk_space_under_threshold: "Attention: l'espace disque disponible sur le serveur est désormais de %{AVAILABLE} MiO" statistics: # outil de statistiques pour les administrateurs diff --git a/config/locales/mails.en.yml b/config/locales/mails.en.yml index 49f505d31..10c3d7fc3 100644 --- a/config/locales/mails.en.yml +++ b/config/locales/mails.en.yml @@ -276,5 +276,9 @@ en: enjoy_a_discount_of_AMOUNT_with_code_CODE: "Enjoy a discount of %{AMOUNT} on the whole site with the code %{CODE}." this_coupon_is_valid_USAGE_times_until_DATE_for_all_your_purchases: "This coupon is valid {USAGE, plural, =1{just once} other{many times}}: for all your purchases {TYPE, select, amount_off{at least equal to the amount of the coupon} other{}}, from now {DATE, select, NO-DATE{and without time limit} other{and until {DATE}}}." + notify_admin_free_disk_space: + subject: "Low disk space" + body: "Warning: available disk space on the server hosting fab-manager is less than %{THRESHOLD} MiB. This can affect its operation and prevent saving some data. Currently, %{AVAILABLE} MiB of free disk space remains available on the mount point." + shared: hello: "Hello %{user_name}" diff --git a/config/locales/mails.es.yml b/config/locales/mails.es.yml index 21d0a6f06..842fcf77b 100644 --- a/config/locales/mails.es.yml +++ b/config/locales/mails.es.yml @@ -275,5 +275,9 @@ es: enjoy_a_discount_of_AMOUNT_with_code_CODE: "Disfruta de un descuento de %{AMOUNT} en toda la web con el código %{CODE}." this_coupon_is_valid_USAGE_times_until_DATE_for_all_your_purchases: "Este cupón es válido {USAGE, plural, =1{just once} other{many times}}: para todas tus compras {TYPE, select, amount_off{at least equal to the amount of the coupon} other{}}, desde ahora {DATE, select, NO-DATE{and without time limit} other{and until {DATE}}}." + notify_admin_free_disk_space: #translation_missing + subject: "Low disk space" + body: "Warning: available disk space on the server hosting fab-manager is less than %{THRESHOLD} MiB. This can affect its operation and prevent saving some data. Currently, %{AVAILABLE} MiB of free disk space remains available on the mount point." + shared: hello: "¡Hola %{user_name}!" diff --git a/config/locales/mails.fr.yml b/config/locales/mails.fr.yml index 52fa22355..aca7d6b80 100644 --- a/config/locales/mails.fr.yml +++ b/config/locales/mails.fr.yml @@ -276,5 +276,10 @@ fr: enjoy_a_discount_of_AMOUNT_with_code_CODE: "Bénéficiez d'une remise de %{AMOUNT} sur tout le site en utilisant le code promo %{CODE}." this_coupon_is_valid_USAGE_times_until_DATE_for_all_your_purchases: "Ce code promo est valable {USAGE, plural, =1{une seule fois} other{plusieurs fois}} : pour tous vos achats {TYPE, select, amount_off{dont le montant est au moins égal à celui du code promo} other{}}, dès maintenant {DATE, select, NO-DATE{et sans limitation de durée} other{et jusqu'au {DATE}}}." + notify_admin_free_disk_space: + subject: "Espace disque faible" + body: "Attention : l'espace disque disponible sur le serveur hébergeant fab-manager est inférieur à %{THRESHOLD} MiO. Cela peut nuire à son bon fonctionnement et empêcher la sauvegarde de certaines données. Actuellement, il reste %{AVAILABLE} MiO d'espace libre sur le point de montage." + + shared: hello: "Bonjour %{user_name}" diff --git a/config/locales/mails.pt.yml b/config/locales/mails.pt.yml index 755f8788e..06becefe3 100755 --- a/config/locales/mails.pt.yml +++ b/config/locales/mails.pt.yml @@ -276,5 +276,9 @@ pt: enjoy_a_discount_of_AMOUNT_with_code_CODE: "Desfrute de um desconto de %{AMOUNT} em todo o site com o código %{CODE}." this_coupon_is_valid_USAGE_times_until_DATE_for_all_your_purchases: "Esse cupom é válido {USAGE, plural, =1{uma vez} other{vérias vezes}}: para todas as suas compras {TYPE, select, amount_off{pelo menos igual ao montante do cupom} other{}}, from now {DATE, select, NO-DATE{e sem limite de tempo} other{até {DATE}}}." + notify_admin_free_disk_space: #translation_missing + subject: "Low disk space" + body: "Warning: available disk space on the server hosting fab-manager is less than %{THRESHOLD} MiB. This can affect its operation and prevent saving some data. Currently, %{AVAILABLE} MiB of free disk space remains available on the mount point." + shared: hello: "Olá %{user_name}" diff --git a/config/locales/pt.yml b/config/locales/pt.yml index d041ecbc6..0cc6cf9c0 100755 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -308,6 +308,8 @@ pt: notify_member_about_coupon: enjoy_a_discount_of_PERCENT_with_code_CODE: "Desfrute de um desconto de %{PERCENT}% com o código %{CODE}" enjoy_a_discount_of_AMOUNT_with_code_CODE: "Desfrute de um desconto de %{AMOUNT} com o código %{CODE}" + notify_admin_free_disk_space: + warning_disk_space_under_threshold: "Warning: the server's available disk space is now %{AVAILABLE} MiB" # missing translation statistics: # statistics tools for admins diff --git a/config/schedule.yml b/config/schedule.yml index e70e4845a..aa9e54101 100644 --- a/config/schedule.yml +++ b/config/schedule.yml @@ -24,4 +24,9 @@ reservation_reminder: class: "ReservationReminderWorker" queue: default +free_disk_space: + cron: "0 5 * * 0" # every sunday at 5am + class: "FreeDiskSpaceWorker" + queue: default + <%= PluginRegistry.insert_code('yml.schedule') %> diff --git a/config/secrets.yml b/config/secrets.yml index d4f4efb3a..189401adf 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -42,6 +42,7 @@ development: facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %> elaticsearch_host: <%= ENV["ELASTICSEARCH_HOST"] %> max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %> + disk_space_mb_alert: <%= ENV["DISK_SPACE_MB_ALERT"] %> test: secret_key_base: 83daf5e7b80d990f037407bab78dff9904aaf3c195a50f84fa8695a22287e707dfbd9524b403b1dcf116ae1d8c06844c3d7ed942564e5b46be6ae3ead93a9d30 @@ -75,6 +76,7 @@ test: facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %> elaticsearch_host: <%= ENV["ELASTICSEARCH_HOST"] %> max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %> + disk_space_mb_alert: <%= ENV["DISK_SPACE_MB_ALERT"] %> staging: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> @@ -116,6 +118,7 @@ staging: facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %> elaticsearch_host: <%= ENV["ELASTICSEARCH_HOST"] %> max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %> + disk_space_mb_alert: <%= ENV["DISK_SPACE_MB_ALERT"] %> # Do not keep production secrets in the repository, # instead read values from the environment. @@ -159,4 +162,5 @@ production: log_level: <%= ENV["LOG_LEVEL"] %> facebook_app_id: <%= ENV["FACEBOOK_APP_ID"] %> elaticsearch_host: <%= ENV["ELASTICSEARCH_HOST"] %> - max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %> \ No newline at end of file + max_image_size: <%= ENV["MAX_IMAGE_SIZE"] %> + disk_space_mb_alert: <%= ENV["DISK_SPACE_MB_ALERT"] %> diff --git a/doc/environment.md b/doc/environment.md index 14a99800c..8733a8c6a 100644 --- a/doc/environment.md +++ b/doc/environment.md @@ -143,6 +143,11 @@ Maximum size (in bytes) allowed for image uploaded on the platform. This parameter concerns events, plans, user's avatars, projects and steps of projects. If this parameter is not specified the maximum size allowed will be 2MB. + DISK_SPACE_MB_ALERT + +Threshold in MB of the minimum free disk space available on the current mount point. +The check will run every weeks and if the threshold is exceeded, an alert will be sent to every administrators. + ADMIN_EMAIL, ADMIN_PASSWORD Credentials for the first admin user created when seeding the project. (not present in application.yml because they are only used once when running the database seed with the command `rake db:seed`)