pvcglue 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +91 -0
- data/Rakefile +1 -0
- data/bin/pvc +13 -0
- data/lib/pvcglue.rb +43 -0
- data/lib/pvcglue/all_the_things.rb +7 -0
- data/lib/pvcglue/bootstrap.rb +8 -0
- data/lib/pvcglue/capistrano.rb +35 -0
- data/lib/pvcglue/cli.rb +150 -0
- data/lib/pvcglue/cloud.rb +278 -0
- data/lib/pvcglue/configuration.rb +157 -0
- data/lib/pvcglue/db.rb +145 -0
- data/lib/pvcglue/deploy.rb +4 -0
- data/lib/pvcglue/env.rb +141 -0
- data/lib/pvcglue/manager.rb +137 -0
- data/lib/pvcglue/nodes.rb +29 -0
- data/lib/pvcglue/packages.rb +47 -0
- data/lib/pvcglue/packages/bootstrap.rb +92 -0
- data/lib/pvcglue/packages/env.rb +80 -0
- data/lib/pvcglue/packages/firewall.rb +48 -0
- data/lib/pvcglue/packages/manager.rb +102 -0
- data/lib/pvcglue/packages/nginx.rb +10 -0
- data/lib/pvcglue/packages/nodejs.rb +17 -0
- data/lib/pvcglue/packages/passenger.rb +28 -0
- data/lib/pvcglue/packages/postgresql.rb +10 -0
- data/lib/pvcglue/packages/role_db.rb +47 -0
- data/lib/pvcglue/packages/role_lb.rb +64 -0
- data/lib/pvcglue/packages/role_memcached.rb +14 -0
- data/lib/pvcglue/packages/role_web.rb +60 -0
- data/lib/pvcglue/packages/rvm.rb +75 -0
- data/lib/pvcglue/packages/timezone.rb +17 -0
- data/lib/pvcglue/packages/ubuntu.rb +100 -0
- data/lib/pvcglue/railtie.rb +11 -0
- data/lib/pvcglue/ssl.rb +37 -0
- data/lib/pvcglue/templates/20auto-upgrades.erb +2 -0
- data/lib/pvcglue/templates/authorized_keys.erb +3 -0
- data/lib/pvcglue/templates/capfile.erb +20 -0
- data/lib/pvcglue/templates/database.yml.erb +57 -0
- data/lib/pvcglue/templates/denial_of_service.erb +3 -0
- data/lib/pvcglue/templates/deploy.rb.erb +81 -0
- data/lib/pvcglue/templates/gemrc.erb +1 -0
- data/lib/pvcglue/templates/hosts.erb +9 -0
- data/lib/pvcglue/templates/lb.nginx.conf.erb +88 -0
- data/lib/pvcglue/templates/lb.sites-enabled.erb +74 -0
- data/lib/pvcglue/templates/maintenance_mode.erb +46 -0
- data/lib/pvcglue/templates/memcached.conf.erb +55 -0
- data/lib/pvcglue/templates/passenger.list.erb +2 -0
- data/lib/pvcglue/templates/pg_hba.conf.erb +101 -0
- data/lib/pvcglue/templates/postgresql.conf.erb +557 -0
- data/lib/pvcglue/templates/sshd_config.erb +91 -0
- data/lib/pvcglue/templates/stage-deploy.rb.erb +33 -0
- data/lib/pvcglue/templates/timezone.erb +1 -0
- data/lib/pvcglue/templates/ufw.rules.erb +42 -0
- data/lib/pvcglue/templates/ufw.rules6.erb +25 -0
- data/lib/pvcglue/templates/web.bashrc.erb +120 -0
- data/lib/pvcglue/templates/web.env.erb +3 -0
- data/lib/pvcglue/templates/web.nginx.conf.erb +82 -0
- data/lib/pvcglue/templates/web.sites-enabled.erb +8 -0
- data/lib/pvcglue/toml_pvc_dumper.rb +53 -0
- data/lib/pvcglue/version.rb +3 -0
- data/pvcglue.gemspec +33 -0
- metadata +296 -0
data/lib/pvcglue/ssl.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module Pvcglue
|
2
|
+
class Ssl < Thor
|
3
|
+
|
4
|
+
desc "csr", "create new csr"
|
5
|
+
|
6
|
+
def csr
|
7
|
+
name = Pvcglue.cloud.app_and_stage_name
|
8
|
+
system("openssl req -new -newkey rsa:2048 -nodes -keyout #{name}.key -out #{name}.csr")
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
desc "import", "import .key or .crt or both if no extension given (.crt must be 'prepared' for nginx)"
|
13
|
+
|
14
|
+
def import(file_name)
|
15
|
+
cloud_data = Pvcglue.cloud.data
|
16
|
+
|
17
|
+
ext = File.extname(file_name)
|
18
|
+
|
19
|
+
case ext
|
20
|
+
when ".crt", ".key"
|
21
|
+
cloud_data[Pvcglue.cloud.app_name][:stages][Pvcglue.cloud.stage_name]["ssl_#{ext[1..-1]}"] = File.read(file_name)
|
22
|
+
when ""
|
23
|
+
cloud_data[Pvcglue.cloud.app_name][:stages][Pvcglue.cloud.stage_name]["ssl_key"] = File.read("#{file_name}.key")
|
24
|
+
cloud_data[Pvcglue.cloud.app_name][:stages][Pvcglue.cloud.stage_name]["ssl_crt"] = File.read("#{file_name}.crt")
|
25
|
+
else
|
26
|
+
raise(Thor::Error, "Unknown file extension: #{ext}.")
|
27
|
+
end
|
28
|
+
|
29
|
+
# File.write(::Pvcglue.cloud.local_file_name, TOML.dump(cloud_data))
|
30
|
+
File.write(::Pvcglue.cloud.local_file_name, TOML::PvcDumper.new(cloud_data).toml_str)
|
31
|
+
|
32
|
+
Pvcglue::Manager.push_configuration
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# This is a generated file. Do not modify...or else! :)
|
2
|
+
|
3
|
+
# Load DSL and Setup Up Stages
|
4
|
+
require 'capistrano/setup'
|
5
|
+
|
6
|
+
# Includes default deployment tasks
|
7
|
+
require 'capistrano/deploy'
|
8
|
+
|
9
|
+
require 'capistrano/rvm'
|
10
|
+
set :rvm_type, :user
|
11
|
+
|
12
|
+
require 'capistrano/bundler'
|
13
|
+
require 'capistrano/rails/assets'
|
14
|
+
require 'capistrano/rails/migrations'
|
15
|
+
<% if Pvcglue.cloud.gems[:whenever] %>
|
16
|
+
require 'whenever/capistrano'
|
17
|
+
<% end %>
|
18
|
+
|
19
|
+
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
|
20
|
+
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# This is a generated file. Do not modify...or else! :)
|
2
|
+
#
|
3
|
+
# Add your username and password to your `.bash_profile` or equivalent, if needed.
|
4
|
+
# (If you don't need a username or password locally for postgresql, you can skip this.)
|
5
|
+
#
|
6
|
+
# Example
|
7
|
+
# export DB_USER_POSTGRES_USERNAME=andrew
|
8
|
+
# export DB_USER_POSTGRES_PASSWORD=andrew
|
9
|
+
#
|
10
|
+
# Note: This will use (or create) a new database for each branch in the project.
|
11
|
+
# Just run `rake db:rebuild` after creating or switching to a new branch.
|
12
|
+
# After that you won't need to rebuild the db when switching branches! :)
|
13
|
+
#
|
14
|
+
# You can disable this with the following if you think this is a bug, instead of a feature. ;)
|
15
|
+
#
|
16
|
+
# export DB_USER_POSTGRES_DISABLE_BRANCHES=true
|
17
|
+
|
18
|
+
server_common: &default_settings
|
19
|
+
adapter: postgresql
|
20
|
+
encoding: utf8
|
21
|
+
username: <%= %{<%= ENV['DB_USER_POSTGRES_USERNAME'] %}+'>' %>
|
22
|
+
password: <%= %{<%= ENV['DB_USER_POSTGRES_PASSWORD'] %}+'>' %>
|
23
|
+
host: <%= %{<%= ENV['DB_USER_POSTGRES_HOST'] || 'localhost' %}+'>' %>
|
24
|
+
port: <%= %{<%= ENV['DB_USER_POSTGRES_PORT'] || '5432' %}+'>' %>
|
25
|
+
|
26
|
+
development:
|
27
|
+
<<: *default_settings
|
28
|
+
database: <%= %{<%= ['#{Pvcglue.cloud.app_name}_dev', ENV['DB_USER_POSTGRES_DISABLE_BRANCHES'] == 'true' ? nil : `git rev-parse --abbrev-ref HEAD`.strip].compact.join('_').downcase %}+'>' %>
|
29
|
+
|
30
|
+
test:
|
31
|
+
<<: *default_settings
|
32
|
+
database: <%= %{<%= ['#{Pvcglue.cloud.app_name}_test', ENV['DB_USER_POSTGRES_DISABLE_BRANCHES'] == 'true' ? nil : `git rev-parse --abbrev-ref HEAD`.strip].compact.join('_').downcase %}+'>' %>
|
33
|
+
|
34
|
+
|
35
|
+
alpha:
|
36
|
+
<<: *default_settings
|
37
|
+
database: <%= Pvcglue.cloud.app_name + '_alpha' %>
|
38
|
+
|
39
|
+
beta:
|
40
|
+
<<: *default_settings
|
41
|
+
database: <%= Pvcglue.cloud.app_name + '_beta' %>
|
42
|
+
|
43
|
+
gamma:
|
44
|
+
<<: *default_settings
|
45
|
+
database: <%= Pvcglue.cloud.app_name + '_gamma' %>
|
46
|
+
|
47
|
+
delta:
|
48
|
+
<<: *default_settings
|
49
|
+
database: <%= Pvcglue.cloud.app_name + '_delta' %>
|
50
|
+
|
51
|
+
preview:
|
52
|
+
<<: *default_settings
|
53
|
+
database: <%= Pvcglue.cloud.app_name + '_preview' %>
|
54
|
+
|
55
|
+
production:
|
56
|
+
<<: *default_settings
|
57
|
+
database: <%= Pvcglue.cloud.app_name + '_production' %>
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# This is a generated file. Do not modify...or else! :)
|
2
|
+
|
3
|
+
set :application, '<%= Pvcglue.configuration.application_name %>'
|
4
|
+
set :repo_url, '<%= Pvcglue.cloud.repo_url %>'
|
5
|
+
|
6
|
+
set :branch, proc { `git rev-parse --abbrev-ref HEAD`.chomp }
|
7
|
+
|
8
|
+
set :linked_dirs, %w{log tmp/pids tmp/cache tmp/sockets}
|
9
|
+
|
10
|
+
set :bundle_flags, '--deployment' # Remove the `--quiet` flag
|
11
|
+
|
12
|
+
namespace :deploy do
|
13
|
+
<% if Pvcglue.cloud.gems[:delayed_job] %>
|
14
|
+
def args
|
15
|
+
fetch(:delayed_job_args, "")
|
16
|
+
end
|
17
|
+
|
18
|
+
def delayed_job_roles
|
19
|
+
fetch(:delayed_job_server_role, :app)
|
20
|
+
end
|
21
|
+
|
22
|
+
desc 'Stop the delayed_job process'
|
23
|
+
task :delayed_job_stop do
|
24
|
+
on roles(delayed_job_roles) do
|
25
|
+
within release_path do
|
26
|
+
with rails_env: fetch(:rails_env) do
|
27
|
+
execute :'script/delayed_job', :stop
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'Start the delayed_job process'
|
34
|
+
task :delayed_job_start do
|
35
|
+
on roles(delayed_job_roles) do
|
36
|
+
within release_path do
|
37
|
+
with rails_env: fetch(:rails_env) do
|
38
|
+
execute :'script/delayed_job', args, :start
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
desc 'Restart the delayed_job process'
|
45
|
+
task :delayed_job_restart do
|
46
|
+
on roles(delayed_job_roles) do
|
47
|
+
within release_path do
|
48
|
+
with rails_env: fetch(:rails_env) do
|
49
|
+
execute :'script/delayed_job', args, :restart
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#desc 'Restart Delayed Job'
|
56
|
+
#task :restart_delayed_job do
|
57
|
+
# on roles(:app), in: :sequence, wait: 5 do
|
58
|
+
# invoke 'delayed_job_restart'
|
59
|
+
# end
|
60
|
+
#end
|
61
|
+
|
62
|
+
#task :restart_delayed_job do
|
63
|
+
# invoke 'delayed_job:restart'
|
64
|
+
#end
|
65
|
+
|
66
|
+
|
67
|
+
after :publishing, :delayed_job_restart # calling this directly is a work-around due to "NoMethodError: undefined method `verbosity'" error when calling task from a task in capistrano 3.1.0 and SSHKit 1.3.0
|
68
|
+
<% end %>
|
69
|
+
|
70
|
+
desc 'Restart passenger app'
|
71
|
+
task :restart_passenger do
|
72
|
+
on roles(:app), in: :sequence, wait: 5 do
|
73
|
+
execute :touch, release_path.join('tmp/restart.txt')
|
74
|
+
#invoke 'delayed_job_restart'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
after :publishing, :restart_passenger
|
79
|
+
|
80
|
+
after :finishing, 'deploy:cleanup'
|
81
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
gem: --no-ri --no-rdoc
|
@@ -0,0 +1,88 @@
|
|
1
|
+
user www-data;
|
2
|
+
|
3
|
+
# TODO: Should be set to the same as `grep processor /proc/cpuinfo | wc -l`
|
4
|
+
worker_processes 1;
|
5
|
+
|
6
|
+
pid /var/run/nginx.pid;
|
7
|
+
|
8
|
+
events {
|
9
|
+
worker_connections 768;
|
10
|
+
# multi_accept on;
|
11
|
+
}
|
12
|
+
|
13
|
+
http {
|
14
|
+
|
15
|
+
##
|
16
|
+
# Basic Settings
|
17
|
+
##
|
18
|
+
|
19
|
+
sendfile on;
|
20
|
+
tcp_nopush on;
|
21
|
+
tcp_nodelay on;
|
22
|
+
keepalive_timeout 65;
|
23
|
+
types_hash_max_size 2048;
|
24
|
+
# server_tokens off;
|
25
|
+
|
26
|
+
server_names_hash_bucket_size 64;
|
27
|
+
# server_name_in_redirect off;
|
28
|
+
|
29
|
+
include /etc/nginx/mime.types;
|
30
|
+
default_type application/octet-stream;
|
31
|
+
|
32
|
+
##
|
33
|
+
# Logging Settings
|
34
|
+
##
|
35
|
+
|
36
|
+
access_log /var/log/nginx/access.log;
|
37
|
+
error_log /var/log/nginx/error.log;
|
38
|
+
|
39
|
+
##
|
40
|
+
# Gzip Settings
|
41
|
+
##
|
42
|
+
|
43
|
+
gzip on;
|
44
|
+
gzip_disable "msie6";
|
45
|
+
|
46
|
+
# gzip_vary on;
|
47
|
+
# gzip_proxied any;
|
48
|
+
# gzip_comp_level 6;
|
49
|
+
# gzip_buffers 16 8k;
|
50
|
+
# gzip_http_version 1.1;
|
51
|
+
# gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
|
52
|
+
|
53
|
+
##
|
54
|
+
# nginx-naxsi config
|
55
|
+
##
|
56
|
+
# Uncomment it if you installed nginx-naxsi
|
57
|
+
##
|
58
|
+
|
59
|
+
# include /etc/nginx/naxsi_core.rules;
|
60
|
+
|
61
|
+
##
|
62
|
+
# Phusion Passenger config
|
63
|
+
##
|
64
|
+
# Uncomment it if you installed passenger or passenger-enterprise
|
65
|
+
##
|
66
|
+
|
67
|
+
# passenger_root /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini;
|
68
|
+
# passenger_ruby /home/deploy/.rvm/gems/ruby-2.0.0-p353/wrappers/ruby;
|
69
|
+
# passenger_ruby /usr/bin/ruby;
|
70
|
+
|
71
|
+
##
|
72
|
+
# Virtual Host Configs
|
73
|
+
##
|
74
|
+
|
75
|
+
# disable the default server
|
76
|
+
#server {
|
77
|
+
# listen 80;
|
78
|
+
# server_name _;
|
79
|
+
# return 444;
|
80
|
+
#}
|
81
|
+
|
82
|
+
# DoS prevention
|
83
|
+
limit_conn_zone $binary_remote_addr zone=conn_limit_per_ip:10m;
|
84
|
+
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=1r/s;
|
85
|
+
|
86
|
+
include /etc/nginx/conf.d/*.conf;
|
87
|
+
include /etc/nginx/sites-enabled/*;
|
88
|
+
}
|
@@ -0,0 +1,74 @@
|
|
1
|
+
upstream <%= Pvcglue.cloud.app_and_stage_name %>_application {
|
2
|
+
<% Pvcglue.cloud.nodes_in_stage('web').each do |_, node_config| %>
|
3
|
+
server <%= node_config[:private_ip] %> max_fails=1 fail_timeout=10s;
|
4
|
+
<% end %>
|
5
|
+
}
|
6
|
+
|
7
|
+
server {
|
8
|
+
listen 80;
|
9
|
+
server_name <%= Pvcglue.cloud.domains.join(' ') %>;
|
10
|
+
|
11
|
+
access_log /var/log/nginx/<%= Pvcglue.cloud.app_and_stage_name %>.access.log;
|
12
|
+
error_log /var/log/nginx/<%= Pvcglue.cloud.app_and_stage_name %>.error.log;
|
13
|
+
|
14
|
+
root <%= Pvcglue.cloud.deploy_to_app_dir %>;
|
15
|
+
|
16
|
+
<%= Pvcglue.render_template('denial_of_service.erb') %>
|
17
|
+
<%= Pvcglue.render_template('maintenance_mode.erb') %>
|
18
|
+
|
19
|
+
<% case Pvcglue.cloud.ssl_mode
|
20
|
+
when :none %>
|
21
|
+
location / {
|
22
|
+
proxy_set_header Host $host;
|
23
|
+
proxy_set_header X-Real-IP $remote_addr;
|
24
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
25
|
+
proxy_set_header X-Maintenance-Bypass $maintenance_bypass;
|
26
|
+
|
27
|
+
proxy_pass http://<%= Pvcglue.cloud.app_and_stage_name %>_application;
|
28
|
+
}
|
29
|
+
<% when :load_balancer_force_ssl %>
|
30
|
+
location / {
|
31
|
+
# According to http://serverfault.com/a/401632/156820 this is the correct way to redirect all http to https
|
32
|
+
return 301 https://$host$request_uri;
|
33
|
+
}
|
34
|
+
<% else
|
35
|
+
raise "Unsupported SSL option '#{Pvcglue.cloud.ssl_mode}'" %>
|
36
|
+
<% end %>
|
37
|
+
}
|
38
|
+
|
39
|
+
<% unless Pvcglue.cloud.ssl_mode == :none %>
|
40
|
+
server {
|
41
|
+
listen 443 ssl;
|
42
|
+
server_name <%= Pvcglue.cloud.domains.join(' ') %>;
|
43
|
+
|
44
|
+
access_log /var/log/nginx/<%= Pvcglue.cloud.app_and_stage_name %>.ssl.access.log;
|
45
|
+
error_log /var/log/nginx/<%= Pvcglue.cloud.app_and_stage_name %>.ssl.error.log;
|
46
|
+
|
47
|
+
root <%= Pvcglue.cloud.deploy_to_app_dir %>;
|
48
|
+
|
49
|
+
<%= Pvcglue.render_template('denial_of_service.erb') %>
|
50
|
+
<%= Pvcglue.render_template('maintenance_mode.erb') %>
|
51
|
+
|
52
|
+
<% case Pvcglue.cloud.ssl_mode
|
53
|
+
when :none %>
|
54
|
+
# ssl_mode: none
|
55
|
+
<% when :load_balancer_force_ssl %>
|
56
|
+
ssl on;
|
57
|
+
ssl_certificate <%= Pvcglue.cloud.nginx_ssl_crt_file_name %>;
|
58
|
+
ssl_certificate_key <%= Pvcglue.cloud.nginx_ssl_key_file_name %>;
|
59
|
+
<% else
|
60
|
+
raise "Unsupported SSL option '#{Pvcglue.cloud.ssl_mode}'" %>
|
61
|
+
<% end %>
|
62
|
+
|
63
|
+
location / {
|
64
|
+
|
65
|
+
proxy_set_header Host $host;
|
66
|
+
proxy_set_header X-Real-IP $remote_addr;
|
67
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
68
|
+
proxy_set_header X-Maintenance-Bypass $maintenance_bypass;
|
69
|
+
proxy_set_header X-Forwarded-Proto https;
|
70
|
+
|
71
|
+
proxy_pass http://<%= Pvcglue.cloud.app_and_stage_name %>_application;
|
72
|
+
}
|
73
|
+
}
|
74
|
+
<% end %>
|
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
# partially based on https://onehub.com/blog/2009/03/06/rails-maintenance-pages-done-right/
|
3
|
+
recursive_error_pages on;
|
4
|
+
|
5
|
+
set $maintenance off;
|
6
|
+
set $maintenance_bypass off;
|
7
|
+
# https://blog.ed.gs/2013/01/25/nginx-multiple-if-statements/
|
8
|
+
set $bypass_test no;
|
9
|
+
|
10
|
+
if (-f $document_root/maintenance/maintenance.on) {
|
11
|
+
set $maintenance on;
|
12
|
+
set $bypass_test yes;
|
13
|
+
}
|
14
|
+
|
15
|
+
if ($remote_addr ~ <%= Pvcglue.cloud.dev_ip_addresses.join('|').gsub('.', '\.') %>) {
|
16
|
+
set $maintenance off;
|
17
|
+
set $bypass_test "${bypass_test}yes";
|
18
|
+
}
|
19
|
+
|
20
|
+
if ($bypass_test = yesyes) {
|
21
|
+
set $maintenance_bypass on; # only add header when maintenance is on and the remote address is a dev ip address
|
22
|
+
}
|
23
|
+
|
24
|
+
if ($uri ~ ^/maintenance/.*) {
|
25
|
+
set $maintenance off;
|
26
|
+
}
|
27
|
+
|
28
|
+
if ($maintenance = on) {
|
29
|
+
return 503; # 503 - Service unavailable
|
30
|
+
}
|
31
|
+
|
32
|
+
location /maintenance {
|
33
|
+
}
|
34
|
+
|
35
|
+
#error_page 404 /404.html;
|
36
|
+
#error_page 500 502 504 /500.html;
|
37
|
+
error_page 503 @503;
|
38
|
+
|
39
|
+
location @503 {
|
40
|
+
|
41
|
+
error_page 405 = /maintenance/maintenance.html;
|
42
|
+
|
43
|
+
# Serve static assets if found.
|
44
|
+
rewrite ^(.*)$ /maintenance/maintenance.html break;
|
45
|
+
}
|
46
|
+
|