-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.rb
328 lines (281 loc) · 7.55 KB
/
app.rb
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
require 'sinatra'
require 'sinatra-initializers'
require 'mongoid'
require "sinatra/config_file"
register Sinatra::Initializers
Mongoid.load!('config/mongoid.yml')
config_file 'config/config.yml'
get '/' do
content_type :html
"ICDC HAProxy API. Take look on <a href='https://git.icdc.io/icdc/haproxy_api'>README</a>\n"
end
namespace '/api/1' do
before do
content_type :json, 'charset' => 'utf-8'
apikey = request.env["HTTP_X_HAPROXYAPI_KEY"] || params[:key]
unless ApiKey.all.collect{|x| x.value}.include?(apikey)
logger.error "Attempt to authorize '#{request.ip}' with API key: '#{apikey}'"
halt 401, {message: "Unauthourized. Use AUTH header to provide valid key"}.to_json
end
@apiv="/api/1"
prepare
end
helpers do
def prepare
@json = nil
return unless ["POST","PUT"].include?(request.request_method)
begin
@json = JSON.parse(request.body.read)
rescue Exception => e
logger.error "Failed not parse request body: #{e.message}"
halt 400, {message: e.message}.to_json
end
end
def store
begin
ConfigManager.update(settings) unless Sinatra::Application.environment == :test
rescue => e
logger.error "Failed to make ConfigManager.update: #{e.message}"
halt 500, {message: e.message}.to_json
end
status 200
{}
end
WEB_ROUTE_PORT = {"http" => 80, "https" => 443}
def new_cfg
# Generate new route
r = Route.new(@json)
return nil if r.nil? || r.host.nil?
# TODO: We do not support other paths yet
r.path = "/"
# Web-route
if is_web_route?(r)
r.port = WEB_ROUTE_PORT[r.proto]
#validate new cfg
return r
end
# TCP-route
if is_tcp_route?(r)
r.port = get_tcp_port
p r.port
return r if r.port <= 9000
end
logger.warn "Invalid route data: #{r.to_json}"
halt 400, {message: "Invalid route data"}.to_json
end
def is_web_route?(r)
WEB_ROUTE_PORT.keys.include?(r.proto)
end
def is_uniq_route?(r)
#If frontend is unique
return !Route.where("proto":r.proto, "host": r.host, "path": r.path).exists?
end
def mongo_id
params[:id].gsub(/[^a-z0-9]/,'')
end
def is_tcp_route?(r)
r.proto == 'tcp'
end
def get_tcp_port
busy_ports = Route.where("proto": "tcp" ).pluck("port")
busy_ports.empty? ? 8000 : busy_ports.max + 1
end
def halt_if_not_found_cert!
@cert = Cert.where(id: mongo_id).first
unless @cert
logger.error "Certificate '#{params[:id]}' not found"
halt 404, { message: "Certificate not found" }.to_json
end
end
def halt_if_not_found_route!
@route = Route.where(id: mongo_id).first
unless @route
logger.error "Route '#{params[:id]}' not found"
halt 404, { message: "Route not found" }.to_json
end
end
def halt_if_not_found_node!
@node = HaproxyNode.where(id: mongo_id).first
unless @node
logger.error "HaproxyNode '#{params[:id]}' not found"
halt 404, { message: "HaproxyNode not found" }.to_json
end
end
end
## Certificates
post '/certs' do
cert = Cert.new(@json)
if cert.save
store
response.headers['Location'] = "#{request.base_url}#{@apiv}/certs/#{cert.id}"
status 201
logger.info "User [#{cert.owner}] added new SSL-certificate [#{cert.name}] for domains #{cert.domains.to_s}"
else
status 422
logger.error "Failed to store cert. Input: #{@json}"
logger.error "Failed to store cert. Output: #{cert.to_json}"
body cert.to_json
end
end
get '/certs' do
ac = Cert.all
[:owner, :domain].each do |filter|
ac = Cert.send(filter, params[filter]) if params[filter]
end
ac.to_json(:except => [:errors, :updated_at])
end
get '/certs/:id' do
halt_if_not_found_cert!
@cert.to_json(:except => [:errors, :updated_at])
end
put '/certs/:id' do
halt_if_not_found_cert!
#TODO: modify
halt 405, {message: "Not implemented yet"}.to_json
end
delete '/certs/:id' do
halt_if_not_found_cert!
logger.info "User #{@cert.owner} removed new SSL-certificate #{@cert.name} for domains #{@cert.domains.to_s}"
@cert.destroy
store
status 204 #No Content
end
## Route API
get "/routes" do
ar = Route.all
[:service, :proto].each do |filter|
ar = Route.send(filter, params[filter]) if params[filter]
end
ar.to_json(:except => [:errors, :updated_at])
end
get "/routes/:id" do
halt_if_not_found_route!
@route.to_json
end
put '/routes/:id' do
halt_if_not_found_route!
@config = new_cfg
@config.id = @route.id
begin
@route.delete
@config.save!
rescue => e
logger.error "Failed to update route: #{e.message}"
halt 500, {message: "Failed to update route"}.to_json
end
logger.info "Route updated: #{@json}"
store
@config.to_json
end
put '/routes/:id/suspend' do
halt_if_not_found_route!
@route.security.status = "suspended"
unless @route.save
logger.error "Failed to suspend route '#{params[:id]}' cause: #{e.message}"
halt 500, {message: "Failed to suspend route"}.to_json
end
logger.info "Route suspended: #{@route.to_json}"
store
@route.to_json
end
put '/routes/:id/set-path' do
halt_if_not_found_route!
if (!is_web_route?(@route))
logger.error "Could not set-path '#{@json}' for TCP route '#{params[:id]}'"
halt 500, {message: "Could not set-path for TCP route"}.to_json
end
path = @json["path"]
opt = Opt.new(name: "http-request", value: "set-path #{path}")
@route.backend.opts.push(opt)
unless @route.save
logger.error "Failed to set-path '#{path}' for route '#{params[:id]}' cause: #{e.message}"
halt 500, {message: "Failed to set-path for route"}.to_json
end
logger.info "set-path for route updated: #{@route.to_json}"
store
@route.to_json
end
post '/routes' do
@config = new_cfg
unless is_uniq_route?(@config)
logger.warn "Duplicated route: #{@config.to_json}"
halt 409, {message: "Route already exists"}.to_json
end
#FIX: apply frequent auto update make cause performance issues
begin
@config.save!
rescue => e
logger.error "Failed to add route: #{@config.to_json}"
halt 500, {message: "Failed to add route"}.to_json
end
logger.info "Route added: #{@json}"
store
@config.to_json
end
delete '/routes/:id' do
halt_if_not_found_route!
logger.info "Route deleted: #{@route.to_json}"
route_data = @route.to_json #FIX: remove when lotus integration will be fixed
@route.delete
store
status 200 #FIX: revert to 204, when deploy permanent fix for lotus integration
#status 204 #No Content
route_data #FIX: temporary fix to send data to lotus integration
end
delete '/routes' do
ar = Route.all
[:service].each do |filter|
ar = Route.send(filter, params[filter]) if params[filter]
end
routes_data = ar.to_json
if ar.any?
logger.info "Routes deleted: #{routes_data}"
ar.delete_all
end
store
#status 204 #No Countent
status 200
routes_data
end
## Push updates
post '/store' do
store
end
## HAproxy Node API
get '/nodes' do
HaproxyNode.all.to_json
end
post '/nodes' do
n = HaproxyNode.new(@json)
begin
n.save!
rescue => e
logger.error "Failed to add haproxy_node: #{@n.to_json}"
halt 500, {message: "Failed to add haproxy_node: #{e.message}"}.to_json
end
n.to_json
end
get '/nodes/:id' do
halt_if_not_found_route!
@node.to_json
end
delete '/nodes/:id' do
halt_if_not_found_route!
@node.delete
status 204
end
##############################################
# OBSOLETE:
## Special ManageIQ integration, by service_id
get "/service/:id" do
r = Route.where("service_id": params[:id].to_i)
r.to_json
end
delete '/service/:id' do
logger.info "Delete all routes for service: #{params[:id]}"
Route.where("service_id": params[:id].to_i).delete_all
store
status 204 #No Content
end
##############################################
end