-
Notifications
You must be signed in to change notification settings - Fork 368
Expand file tree
/
Copy pathsyslog_drain_urls_controller.rb
More file actions
142 lines (118 loc) · 5.3 KB
/
syslog_drain_urls_controller.rb
File metadata and controls
142 lines (118 loc) · 5.3 KB
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
module VCAP::CloudController
class SyslogDrainUrlsInternalController < RestController::BaseController
# Endpoint uses mutual tls for auth, handled by nginx
allow_unauthenticated_access
get '/internal/v5/syslog_drain_urls', :list
def list
prepare_aggregate_function
bindings = ServiceBinding.
join(:apps, guid: :app_guid).
join(:spaces, guid: :apps__space_guid).
join(:organizations, id: :spaces__organization_id).
join(:service_instances, guid: :service_bindings__service_instance_guid).
select(
:service_bindings__syslog_drain_url,
:service_bindings__credentials,
:service_bindings__salt,
:service_bindings__encryption_key_label,
:service_bindings__encryption_iterations,
:service_bindings__app_guid,
:service_bindings__service_instance_guid,
:apps__name___app_name,
:spaces__name___space_name,
:organizations__name___organization_name,
:service_instances__is_gateway_service___is_managed_service
).
where(service_bindings__syslog_drain_url: syslog_drain_urls_query).
each_with_object({}) do |item, injected|
syslog_drain_url = item[:syslog_drain_url]
hostname = hostname_from_app_name(item[:organization_name], item[:space_name], item[:app_name])
app_guid = item[:app_guid]
cert, key, ca = fetch_credentials_from_cache(item)
injected_item = injected[syslog_drain_url] ||= {
url: syslog_drain_url,
binding_data_map: {}
}
cert_item = injected_item[:binding_data_map][[key, cert, ca]] ||= {
cert: cert,
key: key,
ca: ca,
apps: []
}
cert_item[:apps].push({ hostname: hostname, app_id: app_guid })
injected
end.values
bindings.each do |binding|
binding[:credentials] = binding[:binding_data_map].values
binding.delete(:binding_data_map)
end
next_page_token = nil
next_page_token = last_id + batch_size unless bindings.empty?
[HTTP::OK, Oj.dump({ results: bindings, next_id: next_page_token }, mode: :compat)]
end
private
def upsi_service_instance_credential_cache
@upsi_service_instance_credential_cache ||= {}
end
# Service Binding credentials are stored as encrypted values in CCDB.
# Decrypting them is computationally expensive and has caused performance
# issues since this endpoint may decrypt credentials from thousands of service bindings.
#
# Since all user-provided service instance bindings share the same credentials
# as their service instance we can safely cache the for a given service instance guid.
def fetch_credentials_from_cache(item)
if item[:is_managed_service]
credentials = item.credentials
cert = credentials&.fetch('cert', '') || ''
key = credentials&.fetch('key', '') || ''
ca = credentials&.fetch('ca', '') || ''
else # service is user-provided
upsi_service_instance_credential_cache[item.service_instance_guid] ||= {}
if upsi_service_instance_credential_cache[item.service_instance_guid].empty?
credentials = item.credentials
upsi_service_instance_credential_cache[item.service_instance_guid]['cert'] = credentials&.fetch('cert', '') || ''
upsi_service_instance_credential_cache[item.service_instance_guid]['key'] = credentials&.fetch('key', '') || ''
upsi_service_instance_credential_cache[item.service_instance_guid]['ca'] = credentials&.fetch('ca', '') || ''
end
cert = upsi_service_instance_credential_cache[item.service_instance_guid].fetch('cert', '')
key = upsi_service_instance_credential_cache[item.service_instance_guid].fetch('key', '')
ca = upsi_service_instance_credential_cache[item.service_instance_guid].fetch('ca', '')
end
[cert, key, ca]
end
def syslog_drain_urls_query
ServiceBinding.
distinct.
exclude(syslog_drain_url: nil).
exclude(syslog_drain_url: '').
select(:syslog_drain_url).
order(:syslog_drain_url).
limit(batch_size).
offset(last_id)
end
def hostname_from_app_name(*names)
names.map do |name|
name.gsub(/\s+/, '-').gsub(/[^-a-zA-Z0-9]+/, '').sub(/-+$/, '')[0..62]
end.join('.')
end
def aggregate_function(column)
if AppModel.db.database_type == :postgres
Sequel.function(:string_agg, column, ',')
elsif AppModel.db.database_type == :mysql
Sequel.function(:group_concat, column)
else
raise 'Unknown database type'
end
end
def prepare_aggregate_function
return unless AppModel.db.database_type == :mysql
AppModel.db.run('SET SESSION group_concat_max_len = 1000000000')
end
def last_id
Integer(params.fetch('next_id', 0))
end
def batch_size
Integer(params.fetch('batch_size', 50))
end
end
end