diff --git a/.simplecov b/.simplecov index c92cf62e1d..49889aa450 100644 --- a/.simplecov +++ b/.simplecov @@ -7,7 +7,6 @@ SimpleCov.start "rails" do add_filter "lib/tasks/helpers/cfe_payload_recorder.rb" add_filter "lib/tasks/helpers/cy_helper.rb" add_filter "lib/utilities/redis_scanner.rb" - add_filter "app/controllers/pages_controller.rb" enable_coverage :branch diff --git a/README.md b/README.md index 171cf08fa8..e4da73274b 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ The laa-apply-for-legal-aid system is a web service for solicitors that provide - [2. Add a cronjob to run it](#2-add-a-cronjob-to-run-it) - [3. Add the widget to the Geckoboard dashboard](#3-add-the-widget-to-the-geckoboard-dashboard) - [**Troubleshooting**](#troubleshooting) +- [**Maintenance mode**](#docs/maintenance_mode.md) ## Architecture Diagram diff --git a/app/models/setting.rb b/app/models/setting.rb index dcbf31594b..58d4572440 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -31,10 +31,6 @@ def self.linked_applications? setting.linked_applications end - def self.maintenance_mode? - ENV.fetch("MAINTENANCE_MODE", "false") == "true" - end - def self.setting Setting.first || Setting.create! end diff --git a/config/application.rb b/config/application.rb index b97d03c7c3..33db433fa9 100644 --- a/config/application.rb +++ b/config/application.rb @@ -155,6 +155,8 @@ class Application < Rails::Application config.x.redis.oauth_session_url = "#{config.x.redis.base_url}/2" config.x.redis.rack_attack_url = "#{config.x.redis.base_url}/3" + config.x.maintenance_mode = ENV.fetch("MAINTENANCE_MODE", nil)&.downcase&.eql?("true") + # automatically include locale in the query string when generating urls with url_helpers Rails.application.routes.default_url_options[:locale] = I18n.locale config.i18n.default_locale = :en diff --git a/config/routes.rb b/config/routes.rb index 7210192437..b48037c8f2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,7 +4,7 @@ get "status", to: "status#ping", format: :json get "data", to: "status#data" - match "(*any)", to: "pages#servicedown", via: :all if Setting.maintenance_mode? + match "(*any)", to: "pages#servicedown", via: :all if Rails.application.config.x.maintenance_mode root to: "providers/start#index" diff --git a/docs/maintenance_mode.md b/docs/maintenance_mode.md new file mode 100644 index 0000000000..3ca9221eb1 --- /dev/null +++ b/docs/maintenance_mode.md @@ -0,0 +1,22 @@ +## Maintenance mode + +A conditional catchall routes exists in `routes.rb`. This directs all routes requested to the `pages#servicedown` controller and view. To activate the conditional route you must provide the app server with MAINTENANCE_MODE=true. When supplied via the helm values.yml files it must be supplied as a string "true". + +```bash +# activate maintenance mode locally +MAINTENANCE_MODE=true rails s +``` + +You can deploy the app in maintenance mode for the hosted environments by setting it to enabled: "true" in the relevant values.yml files + +```yml +# example values-uat.yaml +maintenance_mode: + enabled: "true" +``` + +To apply via circleCI: + +- commit the above change and push to github +- run through circleCI and deploy to the relevant environment +- to take site out of maintenance you would have to commit the reverse and redeploy via circleCI diff --git a/helm_deploy/apply-for-legal-aid/templates/_envs.tpl b/helm_deploy/apply-for-legal-aid/templates/_envs.tpl index fac8106830..028d1a18b7 100644 --- a/helm_deploy/apply-for-legal-aid/templates/_envs.tpl +++ b/helm_deploy/apply-for-legal-aid/templates/_envs.tpl @@ -382,5 +382,8 @@ env: name: {{ template "apply-for-legal-aid.fullname" . }} key: encryptionKeyDerivationSalt - name: MAINTENANCE_MODE - value: "true" + valueFrom: + secretKeyRef: + name: {{ template "apply-for-legal-aid.fullname" . }} + key: maintenanceMode {{- end }} diff --git a/helm_deploy/apply-for-legal-aid/templates/secret.yaml b/helm_deploy/apply-for-legal-aid/templates/secret.yaml index 89b297935e..4a4a419616 100644 --- a/helm_deploy/apply-for-legal-aid/templates/secret.yaml +++ b/helm_deploy/apply-for-legal-aid/templates/secret.yaml @@ -74,3 +74,4 @@ data: encryptionPrimaryKey: {{ .Values.active_record_encryption.primary_key | b64enc | quote }} encryptionDeterministicKey: {{ .Values.active_record_encryption.deterministic_key | b64enc | quote }} encryptionKeyDerivationSalt: {{ .Values.active_record_encryption.key_derivation_salt | b64enc | quote }} + maintenanceMode: {{ .Values.maintenance_mode.enabled | b64enc | quote }} diff --git a/helm_deploy/apply-for-legal-aid/values-production.yaml b/helm_deploy/apply-for-legal-aid/values-production.yaml index 1d2e0ee2d6..84ad8c18a1 100644 Binary files a/helm_deploy/apply-for-legal-aid/values-production.yaml and b/helm_deploy/apply-for-legal-aid/values-production.yaml differ diff --git a/helm_deploy/apply-for-legal-aid/values-staging.yaml b/helm_deploy/apply-for-legal-aid/values-staging.yaml index ea2d15679c..ff066ece62 100644 Binary files a/helm_deploy/apply-for-legal-aid/values-staging.yaml and b/helm_deploy/apply-for-legal-aid/values-staging.yaml differ diff --git a/helm_deploy/apply-for-legal-aid/values-uat.yaml b/helm_deploy/apply-for-legal-aid/values-uat.yaml index 2f81e508e5..a483b23997 100644 Binary files a/helm_deploy/apply-for-legal-aid/values-uat.yaml and b/helm_deploy/apply-for-legal-aid/values-uat.yaml differ diff --git a/spec/requests/pages_controller_spec.rb b/spec/requests/pages_controller_spec.rb new file mode 100644 index 0000000000..579a0e3ebc --- /dev/null +++ b/spec/requests/pages_controller_spec.rb @@ -0,0 +1,99 @@ +require "rails_helper" + +RSpec.describe PagesController, clamav: true do + context "when not in maintenance mode" do + context "when provider signs in" do + before do + sign_in provider + get root_path + end + + let(:provider) { create(:provider) } + + it "renders root page as expected" do + expect(response).to render_template("providers/start/index") + end + end + end + + context "when in maintenance mode" do + before do + allow(Rails.application.config.x).to receive(:maintenance_mode).and_return(true) + Rails.application.reload_routes! + end + + after do + allow(Rails.application.config.x).to receive(:maintenance_mode).and_return(false) + Rails.application.reload_routes! + end + + shared_examples "maintenance page" do + it { expect(response).to have_http_status(:ok) } + it { expect(response).to render_template("pages/servicedown") } + it { expect(response.body).to include("Sorry, the service is unavailable") } + end + + shared_examples "maintenance json" do + it { expect(response).to have_http_status :service_unavailable } + it { expect(response.body).to include({ error: "Service temporarily unavailable" }.to_json) } + end + + context "when provider signs in" do + before do + sign_in provider + get root_path + end + + let(:provider) { create(:provider) } + + it_behaves_like "maintenance page" + end + + context "when different formats requested" do + context "with html" do + before { get "/", params: { format: :html } } + + it_behaves_like "maintenance page" + end + + context "with json" do + before { get "/", params: { format: :json } } + + it_behaves_like "maintenance json" + end + + context "with ajax" do + before { get "/", xhr: true, params: { format: :js } } + + it_behaves_like "maintenance json" + end + + context "when other format" do + before { get "/", params: { format: :axd } } + + it { expect(response).to have_http_status :service_unavailable } + it { expect(response.body).to include("Service temporarily unavailable") } + end + end + + context "when /ping request made" do + before { get "/ping" } + + it { expect(response).to be_ok } + end + + context "when /healthcheck request made" do + before do + allow(Sidekiq::ProcessSet).to receive(:new).and_return(instance_double(Sidekiq::ProcessSet, size: 1)) + allow(Sidekiq::RetrySet).to receive(:new).and_return(instance_double(Sidekiq::RetrySet, size: 0)) + allow(Sidekiq::DeadSet).to receive(:new).and_return(instance_double(Sidekiq::DeadSet, size: 0)) + connection = instance_double(HTTPClient::Connection) + allow(connection).to receive(:info).and_return(redis_version: "7.0.0") + allow(Sidekiq).to receive(:redis).and_yield(connection) + get "/healthcheck" + end + + it { expect(response).to be_ok } + end + end +end