Git Product home page Git Product logo

turnout's Introduction

Turnout Build Status Code Climate Gem Version

Turnout is Rack middleware with a Ruby on Rails engine that allows you to easily put your app in maintenance mode.

Features

  • Easy installation
  • Rake commands to turn maintenance mode on and off
  • Easily provide a reason for each downtime without editing the maintenance.html file
  • Allow certain IPs or IP ranges to bypass the maintenance page
  • Allow certain paths to be accessible during maintenance
  • Easily override the default maintenance.html file with your own
  • Simple YAML based config file for easy activation, deactivation and configuration without the rake commands
  • Support for multiple maintenance page formats. Current HTML and JSON
  • Supports Rails, Sinatra and any other Rack application
  • Supports multiple maintenance file paths so that groups of applications can be put into maintenance mode at once.

Installation

Rails 3+

In your Gemfile add:

gem 'turnout'

then run

bundle install

Note that you'll need to restart your Rails server before it will work

Sinatra

In your Sinatra app file

require 'rack/turnout'

class App < Sinatra::Base
  configure do
    use Rack::Turnout

In your Rakefile

require 'turnout/rake_tasks'

Activation

rake maintenance:start

or

rake maintenance:start reason="Somebody googled Google!"

or

rake maintenance:start allowed_paths="/login,^/faqs/[0-9]*"

or

rake maintenance:start allowed_ips="4.8.15.16"

or

rake maintenance:start reason="Someone told me I should type <code>sudo rm -rf /</code>" allowed_paths="^/help,^/contact_us" allowed_ips="127.0.0.1,192.168.0.0/24"

or if you've configured named_maintenance_file_paths with a path named server

rake maintenance:server:start

Notes

  • The reason parameter can contain HTML
  • Multiple allowed_paths and allowed_ips can be given. Just comma separate them.
  • All allowed_paths are treated as regular expressions.
  • If you need to use a comma in an allowed_paths regular expression just escape it with a backslash: \,.
  • IP ranges can be given to allowed_ips using CIDR notation.

Deactivation

rake maintenance:end

or if you activated with a named path like server

rake maintenance:server:end

Configuration

Turnout can be configured in two different ways:

  1. Pass a config hash to the middleware

    use Rack::Turnout,
      app_root: '/some/path',
      named_maintenance_file_paths: {app: 'tmp/app.yml', server: '/tmp/server.yml'},
      maintenance_pages_path: 'app/views/maintenance',
      default_maintenance_page: Turnout::MaintenancePage::JSON,
      default_reason: 'Somebody googled Google!',
      default_allowed_paths: ['^/admin/'],
      default_response_code: 418,
      default_retry_after: 3600
  2. Using a config block

    Turnout.configure do |config|
      config.skip_middleware = true
      config.app_root = '/some/path'
      config.named_maintenance_file_paths = {app: 'tmp/app.yml', server: '/tmp/server.yml'}
      config.maintenance_pages_path = 'app/views/maintenance'
      config.default_maintenance_page = Turnout::MaintenancePage::JSON
      config.default_reason = 'Somebody googled Google!'
      config.default_allowed_paths = ['^/admin/']
      config.default_response_code = 418
      config.default_retry_after = 3600
    end

NOTICE: Any custom configuration should be loaded not only in the app but in the rake task. This should happen automatically in Rails as the environment task is run if it exists. But you may want to create your own environment task in non-Rails apps.

Default Configuration

Turnout.configure do |config|
  config.app_root = '.'
  config.named_maintenance_file_paths = {default: config.app_root.join('tmp', 'maintenance.yml').to_s}
  config.maintenance_pages_path = config.app_root.join('public').to_s
  config.default_maintenance_page = Turnout::MaintenancePage::HTML
  config.default_reason = "The site is temporarily down for maintenance.\nPlease check back soon."
  config.default_allowed_paths = []
  config.default_response_code = 503
  config.default_retry_after = 7200
end

Customization

Default maintenance pages are provided, but you can create your own public/maintenance.[html|json|html.erb] files instead. If you provide a reason to the rake task, Turnout will parse the maintenance page file and attempt to replace a Liquid-style {{ reason }} tag with the provided reason. So be sure to include a {{ reason }} tag in your maintenance.html file. In the case of a .html.erb file, reason will be a local variable.

WARNING: The source code of any custom maintenance files you created in the /public directory will be able to be viewed by visiting that URL directly (i.e. http://example.com/maintenance.html.erb). This shouldn't be an issue with HTML and JSON files but with ERB files, it could be. If you're going to use a custom .erb.html file, we recommend you change the maintenance_pages_path setting to something other than the /public directory.

Tips

Denied Paths

There is no denied_paths feature because turnout denies everything by default. However you can achieve the same sort of functionality by using negative lookaheads with the allowed_paths setting, like so:

rake maintenance:start allowed_paths="^(?!/your/under/maintenance/path)"

Multi-App Maintenance

A central named_maintenance_file_path can be configured in all your apps such as /tmp/turnout.yml so that all apps on a server can be put into mainteance mode at once. You could even configure service based paths such as /tmp/mongodb_maintenance.yml so that all apps using MongoDB could be put into maintenance mode.

Detecting Maintenance Mode

If you'd like to detect if maintenance mode is on in your app (for those users or pages that aren't blocked) just call !Turnout::MaintenanceFile.find.nil?.

Behind the Scenes

On every request the Rack app will check to see if tmp/maintenance.yml exists. If the file exists the maintenance page will be shown (unless allowed IPs are given and the requester is in the allowed range).

So if you want to get the maintenance page up or down in a hurry touch tmp/maintenance.yml and rm tmp/maintenance.yml will work.

Turnout will attempt to parse the maintenance.yml file looking for reason, allowed_ip and other settings. The file is checked on every request so you can change these values manually or just rerun the rake maintenance:start command.

Example maintenance.yml File

---
reason: Someone told me I should type <code>sudo rm -rf /</code>
allowed_paths:
- ^/help
- ^/contact_us
allowed_ips:
- 127.0.0.1
- 192.168.0.0/24
response_code: 503
retry_after: 3600

turnout's People

Contributors

adamcrown avatar alainmeier avatar amielvick avatar bfcoder avatar bogdanrada avatar cdimartino avatar charles-busicat avatar cromwellryan avatar crondaemon avatar gitusp avatar hungryzi avatar james05 avatar michaelstephens avatar nog avatar olleolleolle avatar omederos avatar phillbaker avatar pocke avatar rodovich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

turnout's Issues

Any way to redirect to a subdominant?

Is there any way to redirect users to a subdomain when maintenance is on? Let's say instead of a maintainance page they could be redirected to signup.xxxxxxxx.com.

It would be very useful to get a prod env working correctly before allowing access to it.

Great gem btw :)

Add support for redis/db backed configuration

I am running multiple rails servers, it would be awesome if this configuration was hosted in redis. Perhaps multiple other backends as well. Currently I have to enable maintenance mode on each node.

Doesn't work in the production server

Here is what happens if I run this locally in my workstation:

bundle exec rake maintenance:start
Created tmp/maintenance.yml
Run `rake maintenance:end` to stop maintenance mode

And this is what happens when I run it in the production server:

RAILS_ENV=production bundle exec rake maintenance:start
rake aborted!
Don't know how to build task 'maintenance:start'

(See full trace by running task with --trace)

All the other rake tasks works in production, it has to be something related with the environment, could you put some light here so I can research a little bit?

Interface to allow alternative persistence strategy

What do you think if we provide a common interface, so anyone can implement their own persistence strategy?

Turnout is tied too closely to the file strategy.

https://github.com/GoodGuide/maintenance-mode-gem comes into my mind. It provides the following interface:

MaintenanceMode.enable
MaintenanceMode.as_json # => { "enabled": true, "message": null }
MaintenanceMode.enable "we'll be back soon"
MaintenanceMode.as_json # => { "enabled": true, "message": "we'll be back soon" }
MaintenanceMode.enabled? # => true
MaintenanceMode.message # => "we'll be back soon"
MaintenanceMode.disable
MaintenanceMode.enabled? # => false

This would mean I can choose to indicate maintenance by flipping a switch on redis, and fetch message from the database.

Add an option to set the `Retry-After` directive

I was going to use this gem to put my website into maintenance mode, but according to the README (and source code), it only returns a 503, but it is recommended to include the Retry-After header, so Googlebot and others know when they should try to crawl the website again.

Perhaps it is straightforward to include that http header on the response?

Need to restart rails app to make it work.

The gem works fabulously but in my development env I had to restart the server to make this maintenance mode work. why is this happening? will it work fine on production env?

Rake task is not "grepable"

bundle exec rake -T | grep maintenance
# nothing
bundle exec rake -W maintenance:start
# nothing
bundle exec rake maintenance:start
# works just fine

Took me some time to find out what is going on.

Can't get turnout to work on heroku

Is anyone else having trouble using turnout on heroku?
I've just created a new app and deployed it but it only works locally.

This is my gemfile

source 'https://rubygems.org'
ruby '2.2.2'

gem 'rails', '4.2.5.1'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'turnout'

group :development, :test do
  gem 'sqlite3'
  gem 'byebug'
end

group :development do
  gem 'web-console', '~> 2.0'
  gem 'spring'
end

group :production do
  gem 'pg'
  gem 'rails_12factor'
end

And here is turnout.rb in config/initializers


Turnout.configure do |config|
  config.app_root = '.'
  config.named_maintenance_file_paths = {default: config.app_root.join('tmp', 'maintenance.yml').to_s}
  config.default_maintenance_page = Turnout::MaintenancePage::HTML
  config.default_reason = "The site is temporarily down for maintenance.\nPlease check back soon."
  config.default_allowed_paths = []
  config.default_response_code = 503
  config.default_retry_after = 7200
end

maintenance.yml is created when running rake maintenance:start and is deleted on maintenance:end
Still not working. Am I missing something?

Thanks in advance.

Not loading custom maintenance.html

I've been trying to load a custom html file, but haven't had any luck :/

I've tried many different configurations, changing the config.maintenance_pages_path, config.default_maintenance_page but nothing has worked, I'm assuming I'm doing something wrong.

The file seems to be loaded, but I made a few modifications that doesn't show


<header>
      <h1>This is not what the header looks like</h1>
 </header>
2.4.1 :001 > File.expand_path(Turnout::MaintenancePage::HTML.new.send(:custom_path))
"/Users/imartinez/my-fertility-ror-backend/public/maintenance.html"

This is how my initializer looks like

Turnout.configure do |config|
  config.app_root = config.app_root
  config.named_maintenance_file_paths = {default: config.app_root.join('tmp', 'maintenance.yml').to_s}
  config.maintenance_pages_path = config.app_root.join('public').to_s
  config.default_maintenance_page = Turnout::MaintenancePage::HTML
  config.default_reason = "The site is temporarily down for maintenance.\nPlease check back soon."
  config.default_allowed_paths = ['^/admin/','^/admin','^/assets', '^/rails/info/routes', '^/api/v1/maintenance_messages']
  config.default_response_code = 503
  config.default_retry_after = 7200
end

maintenance:end can't delete maintenance.yml

I've been running into an issue on my production server where I put the site into maintenance mode every night to import new data, but when it finishes, the maintenance:end command fails to delete the maintenance.yml file because it's in use by java.exe

I'm running rails on a Windows Server VM using puma.

Is there any way to toggle the maintenance mode without using the yml file? Could I set a flag in active record or something of that nature?

Thanks!

Gem.searcher deprecated in rubygems > 2.0

You get an error when running the rake tasks when you have rubygems > 2.0 installed as they have deprecated Gem.searcher.

undefined method `searcher' for Gem:Module
/Users/buffy/.rvm/gems/ruby-1.9.3-p194/gems/turnout-0.2.3/lib/turnout/rake_tasks.rb:3:in `<top (required)>'
/Users/buffy/.rvm/gems/ruby-1.9.3-p194/gems/backports-3.1.1/lib/backports/tools.rb:328:in `require'

Add support for i18n

At least an how to, because it's a static page so we'll have to do it by JS I think

undefined method 'import' when assets:precompile in prod

When I run assets:precompile for our prod environment

RAILS_ENV=production bin/rake assets:precompile  --trace

I get the following error ...

4: from bin/rake:3:in `<main>'
3: from bin/rake:3:in `require'
2: from ...ruby-2.6.3@.../gems/turnout-2.5.0/lib/turnout/rake_tasks.rb:3:in `<top (required)>'
1: from from ...ruby-2.6.3@.../gems/turnout-2.5.0/lib/turnout/rake_tasks.rb:3:in `each'
from ...ruby-2.6.3@.../gems/turnout-2.5.0/lib/turnout/rake_tasks.rb:3:in 
`block in <top (required)>': undefined method `import' for main:Object (NoMethodError

I'm not sure why assets:precompile is doing this in Rails 5.2.3 but presumably I'm doing something wrong.

Unable to use custom maintenance.json file

I created a new maintenance file in /public/maintenance.json but it doesn't seem to ever get used over the default template.

I tried regenerating all of the files, restarting and playing with app_root scoping, so I'm not sure why it's not working. Is overriding the default maintenance.json file working for anybody else?

capistrano tasks

Could you add please capistrano tasks to enable maintance mode remotely?

Further customization/configuration of maintenance.html

Since Turnout can already replace {{ reason }} inside a custom maintenance.html, I think it would be convenient to provide other replacements to enable more robust customization. I.E. having a hash of replacements that can be configured in the yaml file instead of just reason.

Ideally I'd want to be able to render a haml file and provide variables, but this text replacement may be a more immediate solution.

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can image, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Rake tasks don't appear in rake -T

It would be nice to have the rake tasks listed when I do a rake -T. I though the gem hadn't installed correctly when I didn't see that.

Calls to application API generate error when in maintenance

I have an application that has an API backend that uses versioning and have noticed when I put the application into maintenance using turnout the API calls generate exceptions.

This is the exception generated:

A RuntimeError occurred in #:

  Invalid header value: "application/emop; version=1"
  config/initializers/quiet_assets.rb:6:in `call_with_quiet_assets'

The code using the API is setting the "Accept" HTTP header to be "application/emop; version=1".

The Rails application is here - https://github.com/Early-Modern-OCR/emop-dashboard

Let me know if this is specific to turnout or if this could be caused by another issue. The issue only happens when maintenance is enabled.

Thanks,

  • Trey

Maintenance helper

I was wondering if there was a helper or something similar that I could use to display a UI hint in the application layout that maintenance mode is activated. Obviously that would be for those IPs that had been permitted.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.