Show a maintenance window
Per-database-server offsets will be added in a separate commit refs FOO-1600 Change-Id: I836691ba18b373717f139e116b9b91376f67b682 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/260356 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Ethan Vizitei <evizitei@instructure.com> QA-Review: Jacob Burroughs <jburroughs@instructure.com> Product-Review: Jacob Burroughs <jburroughs@instructure.com>
This commit is contained in:
parent
208407d180
commit
16b1a67f8e
|
@ -128,6 +128,8 @@ gem 'twilio-ruby', '5.36.0', require: false
|
|||
gem 'tzinfo', '1.2.7'
|
||||
gem 'vault', '0.15.0', require: false
|
||||
gem 'vericite_api', '1.5.3'
|
||||
gem 'week_of_month', '1.2.5',
|
||||
github: 'instructure/week-of-month', ref: 'b3013639e9474f302b5a6f27e4e45313e8d24902'
|
||||
gem 'will_paginate', '3.3.0', require: false # required for folio-pagination
|
||||
|
||||
path 'engines' do
|
||||
|
|
|
@ -134,6 +134,12 @@
|
|||
<%= f.time_zone_select :default_time_zone, I18nTimeZone.us_zones, :model => I18nTimeZone %>
|
||||
</td>
|
||||
</tr>
|
||||
<% if @account.shard.database_server.maintenance_window_start_hour %>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td> <%= render partial: 'shared/maintenance_window', locals: { database_server: @account.shard.database_server } %> </td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<%= f.fields_for :settings do |settings| %>
|
||||
<% if @account.grants_right?(@current_user, :manage_site_settings) %>
|
||||
<tr>
|
||||
|
|
|
@ -157,6 +157,12 @@
|
|||
<span class="edit_data"><%= f.time_zone_select :time_zone, I18nTimeZone.us_zones, :model => I18nTimeZone, :default => (@domain_root_account.try(:default_time_zone) || "Mountain Time (US & Canada)") %></span>
|
||||
</td>
|
||||
</tr>
|
||||
<% if @user.shard.database_server.maintenance_window_start_hour %>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td> <%= render partial: 'shared/maintenance_window', locals: { database_server: @user.shard.database_server } %> </td>
|
||||
</tr>
|
||||
<% end %>
|
||||
<% if @domain_root_account == Account.default %>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
<% next_window = database_server.next_maintenance_window %>
|
||||
<%= t("Maintenance windows: %{days} %{weekday} of the month from %{time_range} (%{utc_time_range} UTC )",
|
||||
{ days: database_server.maintenance_window_weeks_of_month.map(&:ordinalize).to_sentence,
|
||||
weekday: I18n.t('date.day_names')[next_window[0].wday],
|
||||
time_range: time_string(next_window[0], next_window[1]),
|
||||
utc_time_range: time_string(next_window[0], next_window[1], ActiveSupport::TimeZone['UTC']) }) %>
|
||||
<br/>
|
||||
<%= t("Next maintenance at: %{window}", {
|
||||
window: datetime_string(next_window[0], :verbose, next_window[1], with_weekday: true) }) %>
|
|
@ -142,6 +142,43 @@ Rails.application.config.after_initialize do
|
|||
@in_current_region
|
||||
end
|
||||
|
||||
def next_maintenance_window
|
||||
return nil unless maintenance_window_start_hour
|
||||
|
||||
start_day = DateTime.now
|
||||
# This array is effectively 1 indexed
|
||||
relevant_weeks = maintenance_window_weeks_of_month.map { |i| WeekOfMonth::Constant::WEEKS_IN_SEQUENCE[i] }
|
||||
maintenance_days = relevant_weeks.map do |ordinal|
|
||||
Time.zone.local_to_utc(start_day.send("#{ordinal}_#{maintenance_window_weekday}_in_month".downcase))
|
||||
end + relevant_weeks.map do |ordinal|
|
||||
Time.zone.local_to_utc((start_day + 1.month).send("#{ordinal}_#{maintenance_window_weekday}_in_month".downcase))
|
||||
end
|
||||
|
||||
next_day = maintenance_days.find { |d| d.future? }
|
||||
# Time offsets are strange
|
||||
start_at = next_day.utc.beginning_of_day - maintenance_window_start_hour.hours
|
||||
end_at = start_at + maintenance_window_duration
|
||||
|
||||
[start_at, end_at]
|
||||
end
|
||||
|
||||
def maintenance_window_start_hour
|
||||
Setting.get('maintenance_window_start_hour', nil)&.to_i
|
||||
end
|
||||
|
||||
def maintenance_window_duration
|
||||
# ISO 8601 duration
|
||||
ActiveSupport::Duration.parse(Setting.get('maintenance_window_duration', "PT2H"))
|
||||
end
|
||||
|
||||
def maintenance_window_weekday
|
||||
Setting.get('maintenance_window_weekday', 'thursday').downcase
|
||||
end
|
||||
|
||||
def maintenance_window_weeks_of_month
|
||||
Setting.get('maintenance_window_weeks_of_month', "1,3").split(',').map(&:to_i)
|
||||
end
|
||||
|
||||
def self.send_in_each_region(klass, method, enqueue_args = {}, *args)
|
||||
run_current_region_asynchronously = enqueue_args.delete(:run_current_region_asynchronously)
|
||||
|
||||
|
|
|
@ -60,9 +60,9 @@ module TextHelper
|
|||
end
|
||||
end
|
||||
|
||||
def datetime_string(start_datetime, datetime_type=:event, end_datetime=nil, shorten_midnight=false, zone=nil)
|
||||
def datetime_string(start_datetime, datetime_type=:event, end_datetime=nil, shorten_midnight=false, zone=nil, with_weekday: false)
|
||||
zone ||= ::Time.zone
|
||||
presenter = Utils::DatetimeRangePresenter.new(start_datetime, end_datetime, datetime_type, zone)
|
||||
presenter = Utils::DatetimeRangePresenter.new(start_datetime, end_datetime, datetime_type, zone, with_weekday: with_weekday)
|
||||
presenter.as_string(shorten_midnight: shorten_midnight)
|
||||
end
|
||||
|
||||
|
|
|
@ -18,13 +18,14 @@
|
|||
|
||||
module Utils
|
||||
class DatePresenter
|
||||
attr_reader :date, :raw_date, :zone
|
||||
attr_reader :date, :raw_date, :zone, :with_weekday
|
||||
|
||||
def initialize(date, zone=nil)
|
||||
def initialize(date, zone=nil, with_weekday: false)
|
||||
zone ||= Time.zone
|
||||
@raw_date = date
|
||||
@date = RelativeDate.new(date, zone)
|
||||
@zone = zone
|
||||
@with_weekday = with_weekday
|
||||
end
|
||||
|
||||
def as_string(style=:normal)
|
||||
|
@ -52,7 +53,8 @@ module Utils
|
|||
end
|
||||
|
||||
def i18n_date(format)
|
||||
I18n.l(raw_date, format: I18n.send(:t, :"date.formats.#{format}"))
|
||||
# Use send to prevent i18nliner trying to parse this
|
||||
I18n.l(raw_date, format: I18n.send(:t, "date.formats.#{format}#{with_weekday ? '_with_weekday' : ''}"))
|
||||
end
|
||||
|
||||
def special_value_type
|
||||
|
|
|
@ -18,13 +18,14 @@
|
|||
|
||||
module Utils
|
||||
class DatetimeRangePresenter
|
||||
attr_reader :start, :zone
|
||||
def initialize(datetime, end_datetime = nil, datetime_type=:event, zone=nil)
|
||||
attr_reader :start, :zone, :with_weekday
|
||||
def initialize(datetime, end_datetime = nil, datetime_type=:event, zone=nil, with_weekday: false)
|
||||
zone ||= ::Time.zone
|
||||
@start = datetime.in_time_zone(zone) rescue datetime
|
||||
@_finish = end_datetime.in_time_zone(zone) rescue end_datetime
|
||||
@_datetime_type = datetime_type
|
||||
@zone = zone
|
||||
@with_weekday = with_weekday
|
||||
end
|
||||
|
||||
def as_string(options={})
|
||||
|
@ -94,7 +95,7 @@ module Utils
|
|||
end
|
||||
|
||||
def present_date(date)
|
||||
Utils::DatePresenter.new(date.to_date, zone).as_string(date_style)
|
||||
Utils::DatePresenter.new(date.to_date, zone, with_weekday: with_weekday).as_string(date_style)
|
||||
end
|
||||
|
||||
def finish
|
||||
|
|
|
@ -40,4 +40,68 @@ describe Switchman::Shard do
|
|||
expect(Shard.in_region('eu-west-1')).to eq([s3])
|
||||
end
|
||||
end
|
||||
|
||||
describe "maintenance_windows" do
|
||||
before do
|
||||
allow(Setting).to receive(:get).and_call_original
|
||||
end
|
||||
|
||||
it 'Returns an empty window if no start is defined' do
|
||||
allow(Setting).to receive(:get).with("maintenance_window_start_hour", anything).and_return(nil)
|
||||
|
||||
expect(DatabaseServer.all.first.next_maintenance_window).to be(nil)
|
||||
end
|
||||
|
||||
it 'Returns a window of the correct duration' do
|
||||
allow(Setting).to receive(:get).with("maintenance_window_start_hour", anything).and_return('0')
|
||||
allow(Setting).to receive(:get).with("maintenance_window_duration", anything).and_return('PT3H')
|
||||
|
||||
window = DatabaseServer.all.first.next_maintenance_window
|
||||
|
||||
expect(window[1] - window[0]).to eq(3.hours)
|
||||
end
|
||||
|
||||
it 'Returns a window starting at the correct time' do
|
||||
allow(Setting).to receive(:get).with("maintenance_window_start_hour", anything).and_return('3')
|
||||
|
||||
window = DatabaseServer.all.first.next_maintenance_window
|
||||
|
||||
expect(window[0].utc.hour).to eq(21)
|
||||
|
||||
allow(Setting).to receive(:get).with("maintenance_window_start_hour", anything).and_return('-7')
|
||||
|
||||
window = DatabaseServer.all.first.next_maintenance_window
|
||||
|
||||
expect(window[0].utc.hour).to eq(7)
|
||||
end
|
||||
|
||||
it 'Returns a window on the correct day' do
|
||||
allow(Setting).to receive(:get).with("maintenance_window_start_hour", anything).and_return('0')
|
||||
allow(Setting).to receive(:get).with("maintenance_window_weekday", anything).and_return('Tuesday')
|
||||
|
||||
window = DatabaseServer.all.first.next_maintenance_window
|
||||
|
||||
expect(window[0].wday).to eq(Date::DAYNAMES.index('Tuesday'))
|
||||
end
|
||||
|
||||
it 'Returns a window on the correct day of the month' do
|
||||
allow(Setting).to receive(:get).with("maintenance_window_start_hour", anything).and_return('0')
|
||||
allow(Setting).to receive(:get).with("maintenance_window_weekday", anything).and_return('Tuesday')
|
||||
allow(Setting).to receive(:get).with("maintenance_window_weeks_of_month", anything).and_return('2,4')
|
||||
|
||||
Timecop.freeze(Time.utc(2021,3,1,12,0)) do
|
||||
window = DatabaseServer.all.first.next_maintenance_window
|
||||
|
||||
# The 9th was the second tuesday of that month
|
||||
expect(window[0].day).to eq(9)
|
||||
end
|
||||
|
||||
Timecop.freeze(Time.utc(2021,3,10,12,0)) do
|
||||
window = DatabaseServer.all.first.next_maintenance_window
|
||||
|
||||
# The 23rd was the fourth tuesday of that month
|
||||
expect(window[0].day).to eq(23)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue