/
deploy
executable file
·169 lines (145 loc) · 4.83 KB
/
deploy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env ruby
require 'fileutils'
require 'uri'
require 'net/http'
if ENV['FLY_APP_NAME']
# ensure machine is the only one in its region
machines_by_region = `dig +short txt _instances.internal`.
chomp.gsub('" "', '').gsub('"', '').split(';').
map {|instance| instance.scan(/(\w+)=([^,]+)/).to_h}.
select {|instance| instance['app'] == ENV["FLY_APP_NAME"]}.
reject {|instance| instance['instance'] == ENV["FLY_ALLOC_ID"]}.
sort_by {|instance| instance['instance']}.
map {|instance| [instance['instance'], instance['region']]}.
sort_by(&:first).group_by(&:last).
map {|region, instances| [region, instances.first.first]}.to_h
if machines_by_region.values.include? ENV["FLY_ALLOC_ID"]
STDERR.puts "Another machine exists in this region, exiting"
exit 1
end
# show maintenance mode
system "nginx -c /rails/config/nginx.startup"
# start rsync daemon
system "rsync --daemon"
# setup volume, .ssh, db, storage directories
VOLUME = File.dirname(ENV.fetch('RAILS_DB_VOLUME', '/data/db'))
FileUtils.chown('rails', 'rails', VOLUME)
FileUtils.mkdir_p "#{VOLUME}/.ssh"
FileUtils.mkdir_p ENV['RAILS_DB_VOLUME']
FileUtils.mkdir_p ENV['RAILS_STORAGE']
# change ownership of data files
FileUtils.chown_R 'rails', 'rails', [
ENV['RAILS_DB_VOLUME'],
ENV['RAILS_STORAGE']
]
# openssh: fly environment variables
IO.write '/etc/environment',
ENV.select{|key, value| key =~ /^FLY_*|PRIMARY_REGION/}.
map {|key, value| "#{key}=#{value}\n"}.join
# openssh: install authorized key and host keys
Dir.chdir "#{VOLUME}/.ssh" do
# install authorized key to allow ssh in
if File.exist? "authorized_keys"
if not Dir.exist? "/home/rails/.ssh"
FileUtils.mkdir_p "/home/rails/.ssh"
FileUtils.chmod 0700, "/home/rails/.ssh"
FileUtils.chown "rails", "rails", "/home/rails/.ssh"
end
if not File.exist? "/home/rails/.ssh/authorized_keys"
FileUtils.cp "authorized_keys", "/home/rails/.ssh/authorized_keys"
FileUtils.chown "rails", "rails", "/home/rails/.ssh/authorized_keys"
end
end
# ensure host keys remain stable
host_keys = Dir['ssh_host*']
if host_keys.empty?
# generate new keys
Dir["/etc/ssh/ssh_host*.key"].each do key
type = key[/.*_(\w+)_key/, 1]
FileUtils.rm [key, "{key}.pub"]
system "ssh-keygen -q -N '' -t #{type} -f #{key}"
end
# save keys on volume
FileUtils.cp Dir["/etc/ssh/ssh_host*"], Dir.pwd
else
# restore keys from volume
host_keys.each do |key|
if File.read(key) != File.read("/etc/ssh/#{key}")
FileUtils.cp key, "/etc/ssh/#{key}", preserve: true
end
end
end
end
# start ssyd
system "/usr/sbin/sshd"
# Sync data from primary region
if ENV['FLY_REGION'] != ENV['PRIMARY_REGION']
source = "rsync://#{ENV['PRIMARY_REGION']}.#{ENV['FLY_APP_NAME']}.internal"
# get authorized keys
authkeys = "#{VOLUME}/.ssh/authorized_keys"
if not File.exist? authkeys
system *%W(
rsync
-av
#{source}/ssh/authorized_keys
#{authkeys}
)
end
# synch databases and storage
system *%W(
rsync
-av
--update
#{source}#{VOLUME}/db
#{source}#{VOLUME}/storage
#{VOLUME}
)
end
LATEST = Dir["#{VOLUME}/db/2*.sqlite3"].map {|name| File.mtime(name) rescue Time.at(0)}.max
end
# set up nginx and run migrations
Dir.chdir File.dirname(__dir__) do
system "#{RbConfig.ruby} ./config/tenant/nginx-config.rb"
showcase_conf = '/etc/nginx/sites-enabled/showcase.conf'
if IO.read(showcase_conf).include? '410'
fork do
10.times do
sleep 30
STDERR.puts "Reconfiguring nginx..."
system "#{RbConfig.ruby} ./config/tenant/nginx-config.rb"
exit unless IO.read(showcase_conf).include? '410'
end
end
end
end
# Sync databases back to primary region
if ENV['FLY_APP_NAME']
if Dir["#{VOLUME}/db/2*.sqlite3"].map {|name| File.mtime(name) rescue Time.at(0)}.max > LATEST
if ENV['FLY_REGION'] != ENV['PRIMARY_REGION']
dest = "rsync://#{ENV['PRIMARY_REGION']}.#{ENV['FLY_APP_NAME']}.internal"
# synch databases and storage
system *%W(
rsync
-av
--update
--exclude .time
#{VOLUME}/db/
#{dest}#{VOLUME}/db/
)
end
# run webhook
uri = URI('https://rubix.intertwingly.net/webhook/showcase')
res = Net::HTTP.get_response(uri)
if res.is_a?(Net::HTTPSuccess)
puts res.body
else
STDERR.puts res
STDERR.puts res.body
end
end
# Configure memory for redis
# https://redis.io/docs/getting-started/faq/#background-saving-fails-with-a-fork-error-on-linux
File.write '/proc/sys/vm/overcommit_memory', '1'
# exit maintenance mode
system "nginx -c /rails/config/nginx.startup -s stop"
end