detector 0.3.0 → 0.4.0
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 +4 -4
- data/bin/detector +10 -2
- data/lib/detector/addons/mariadb.rb +4 -2
- data/lib/detector/addons/mysql.rb +37 -4
- data/lib/detector/addons/postgres.rb +52 -6
- data/lib/detector/addons/redis.rb +20 -0
- data/lib/detector/base.rb +22 -268
- data/lib/detector/region.rb +240 -0
- data/lib/detector/vendor.rb +82 -0
- data/lib/detector/version.rb +1 -1
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2fdcd9559b7c9d9d40b8908d4ec47ef17ff63d62305832bbe7f69b10b05a35b
|
4
|
+
data.tar.gz: 0560305f80f5d1f5aea6eb5959ef7ebb3cc225479492a69594bf227d618804f1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82be9fdd64b14e11e3ff0ff0d927b2508129eb0a84d5a5a4340c7afd991273ddd83f114f6ffdfb1d2add0e84a9526ba8ccd727887ecf70d694f4fb8cc2a0e053
|
7
|
+
data.tar.gz: 53d7ee213ff018c23a7df00df47d8a3cf8525bb475a9eafb4415ee8ba9ffc4db3c2c22dbc48711f06fa2a390ef1f92a3fbf215451a2460022d34e3f23174bf79
|
data/bin/detector
CHANGED
@@ -23,6 +23,10 @@ puts "Detected: #{detector.kind}"
|
|
23
23
|
puts "Version: #{detector.version}"
|
24
24
|
puts "Host: #{detector.host}:#{detector.port}"
|
25
25
|
|
26
|
+
if detector.respond_to?(:replication_available?) && !detector.replication_available?.nil?
|
27
|
+
puts "Replication: #{detector.replication_available? ? 'Available' : 'Not available'}"
|
28
|
+
end
|
29
|
+
|
26
30
|
if detector.user_access_level
|
27
31
|
puts "User access level: #{detector.user_access_level}"
|
28
32
|
end
|
@@ -54,9 +58,13 @@ if detector.databases?
|
|
54
58
|
puts "\nDatabase: #{db_name} (#{db[:size]})"
|
55
59
|
|
56
60
|
if detector.tables?
|
57
|
-
|
58
|
-
|
61
|
+
if db[:table_count]
|
62
|
+
puts " Tables: #{db[:table_count]}"
|
63
|
+
else
|
64
|
+
puts " Tables: #{detector.table_count(db_name)}"
|
65
|
+
end
|
59
66
|
|
67
|
+
tables = detector.tables(db_name).first(3)
|
60
68
|
tables.each do |table|
|
61
69
|
puts " - #{table[:name]}: #{table[:row_count]} rows (#{table[:size]})"
|
62
70
|
end
|
@@ -31,7 +31,8 @@ module Detector
|
|
31
31
|
db_list.each do |db_name|
|
32
32
|
size_query = "SELECT
|
33
33
|
IFNULL(FORMAT(SUM(data_length + index_length) / 1024 / 1024, 2), '0.00') AS size_mb,
|
34
|
-
IFNULL(SUM(data_length + index_length), 0) AS raw_size
|
34
|
+
IFNULL(SUM(data_length + index_length), 0) AS raw_size,
|
35
|
+
COUNT(table_name) AS table_count
|
35
36
|
FROM information_schema.TABLES
|
36
37
|
WHERE table_schema = '#{db_name}'"
|
37
38
|
|
@@ -39,7 +40,8 @@ module Detector
|
|
39
40
|
result << {
|
40
41
|
name: db_name,
|
41
42
|
size: "#{size_data['size_mb']} MB",
|
42
|
-
raw_size: size_data['raw_size'].to_i
|
43
|
+
raw_size: size_data['raw_size'].to_i,
|
44
|
+
table_count: size_data['table_count'].to_i
|
43
45
|
}
|
44
46
|
end
|
45
47
|
|
@@ -44,15 +44,22 @@ module Detector
|
|
44
44
|
|
45
45
|
def databases
|
46
46
|
return [] unless connection
|
47
|
-
@databases ||= connection.query("SELECT
|
47
|
+
@databases ||= connection.query("SELECT
|
48
|
+
schema_name AS name,
|
48
49
|
FORMAT(SUM(data_length + index_length) / 1024 / 1024, 2) AS size_mb,
|
49
|
-
SUM(data_length + index_length) AS raw_size
|
50
|
+
SUM(data_length + index_length) AS raw_size,
|
51
|
+
COUNT(table_name) AS table_count
|
50
52
|
FROM information_schema.SCHEMATA
|
51
|
-
JOIN information_schema.TABLES ON table_schema = schema_name
|
53
|
+
LEFT JOIN information_schema.TABLES ON table_schema = schema_name
|
52
54
|
WHERE schema_name NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
|
53
55
|
GROUP BY schema_name
|
54
56
|
ORDER BY raw_size DESC").map do |row|
|
55
|
-
{
|
57
|
+
{
|
58
|
+
name: row['name'],
|
59
|
+
size: "#{row['size_mb']} MB",
|
60
|
+
raw_size: row['raw_size'].to_i,
|
61
|
+
table_count: row['table_count'].to_i
|
62
|
+
}
|
56
63
|
end
|
57
64
|
end
|
58
65
|
|
@@ -154,6 +161,32 @@ module Detector
|
|
154
161
|
"Limited access"
|
155
162
|
end
|
156
163
|
end
|
164
|
+
|
165
|
+
def replication_available?
|
166
|
+
return nil unless connection
|
167
|
+
|
168
|
+
begin
|
169
|
+
# Check master status
|
170
|
+
master_result = connection.query("SHOW MASTER STATUS")
|
171
|
+
return true if master_result.count > 0
|
172
|
+
|
173
|
+
# Check if this is a slave
|
174
|
+
slave_result = connection.query("SHOW SLAVE STATUS")
|
175
|
+
return true if slave_result.count > 0
|
176
|
+
|
177
|
+
# Check if replication user exists or if binary logging is enabled
|
178
|
+
repl_users = connection.query("SELECT user FROM mysql.user WHERE Repl_slave_priv = 'Y'")
|
179
|
+
return true if repl_users.count > 0
|
180
|
+
|
181
|
+
# Check if binary logging is enabled (needed for replication)
|
182
|
+
binary_log = connection.query("SHOW VARIABLES LIKE 'log_bin'").first
|
183
|
+
return true if binary_log && binary_log['Value'] && binary_log['Value'].downcase == 'on'
|
184
|
+
|
185
|
+
false
|
186
|
+
rescue => e
|
187
|
+
nil
|
188
|
+
end
|
189
|
+
end
|
157
190
|
end
|
158
191
|
end
|
159
192
|
|
@@ -92,13 +92,48 @@ module Detector
|
|
92
92
|
|
93
93
|
def databases
|
94
94
|
return [] unless connection
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
result = []
|
96
|
+
|
97
|
+
# Get the list of databases and their sizes
|
98
|
+
db_list = connection.exec("SELECT datname, pg_size_pretty(pg_database_size(datname)) as size,
|
99
|
+
pg_database_size(datname) as raw_size
|
100
|
+
FROM pg_database
|
101
|
+
WHERE datistemplate = false
|
102
|
+
ORDER BY raw_size DESC")
|
103
|
+
|
104
|
+
# For each database, get table count
|
105
|
+
db_list.each do |row|
|
106
|
+
db_name = row['datname']
|
107
|
+
|
108
|
+
# Skip system databases or databases we can't connect to
|
109
|
+
next if ['postgres', 'template0', 'template1'].include?(db_name)
|
110
|
+
|
111
|
+
# Get table count for this database
|
112
|
+
table_count = 0
|
113
|
+
|
114
|
+
begin
|
115
|
+
# Create a temporary connection to count tables
|
116
|
+
temp_conn = PG::Connection.new(host: host, port: port, user: uri.user,
|
117
|
+
password: uri.password, dbname: db_name) rescue nil
|
118
|
+
|
119
|
+
if temp_conn
|
120
|
+
table_count = temp_conn.exec("SELECT count(*) FROM information_schema.tables WHERE table_schema = 'public'").first['count'].to_i
|
121
|
+
temp_conn.close
|
122
|
+
end
|
123
|
+
rescue
|
124
|
+
# Skip if we can't connect
|
125
|
+
next
|
126
|
+
end
|
127
|
+
|
128
|
+
result << {
|
129
|
+
name: db_name,
|
130
|
+
size: row['size'],
|
131
|
+
raw_size: row['raw_size'].to_i,
|
132
|
+
table_count: table_count
|
133
|
+
}
|
101
134
|
end
|
135
|
+
|
136
|
+
@databases = result
|
102
137
|
end
|
103
138
|
|
104
139
|
def connection_count
|
@@ -158,6 +193,17 @@ module Detector
|
|
158
193
|
end
|
159
194
|
end
|
160
195
|
end
|
196
|
+
|
197
|
+
def replication_available?
|
198
|
+
return nil unless connection
|
199
|
+
|
200
|
+
begin
|
201
|
+
replication_roles = connection.exec("SELECT rolname, rolreplication FROM pg_roles WHERE rolreplication = true;")
|
202
|
+
!replication_roles.values.empty?
|
203
|
+
rescue => e
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
end
|
161
207
|
end
|
162
208
|
end
|
163
209
|
|
@@ -151,6 +151,26 @@ module Detector
|
|
151
151
|
end
|
152
152
|
end
|
153
153
|
end
|
154
|
+
|
155
|
+
def replication_available?
|
156
|
+
return nil unless connection && info
|
157
|
+
|
158
|
+
begin
|
159
|
+
# Check if this is a master in a replication setup
|
160
|
+
if info['role'] == 'master'
|
161
|
+
return true
|
162
|
+
end
|
163
|
+
|
164
|
+
# Check if server has replication enabled
|
165
|
+
if info['connected_slaves'].to_i > 0 || info['slave_read_only'] == '0'
|
166
|
+
return true
|
167
|
+
end
|
168
|
+
|
169
|
+
false
|
170
|
+
rescue => e
|
171
|
+
nil
|
172
|
+
end
|
173
|
+
end
|
154
174
|
end
|
155
175
|
end
|
156
176
|
|
data/lib/detector/base.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'geocoder'
|
3
|
+
require_relative 'region'
|
4
|
+
require_relative 'vendor'
|
3
5
|
|
4
6
|
module Detector
|
5
7
|
class Base
|
@@ -108,201 +110,7 @@ module Detector
|
|
108
110
|
|
109
111
|
def region
|
110
112
|
return nil unless valid?
|
111
|
-
|
112
|
-
# Try to determine region from hostname first
|
113
|
-
hostname = host.to_s.downcase
|
114
|
-
|
115
|
-
# AWS regions from hostname
|
116
|
-
if hostname =~ /amazonaws\.com/ || hostname =~ /aws/
|
117
|
-
return "us-east-1" if hostname =~ /us-east-1|virginia|nova/
|
118
|
-
return "us-east-2" if hostname =~ /us-east-2|ohio/
|
119
|
-
return "us-west-1" if hostname =~ /us-west-1|california|norcal/
|
120
|
-
return "us-west-2" if hostname =~ /us-west-2|oregon/
|
121
|
-
return "af-south-1" if hostname =~ /af-south|cape-town/
|
122
|
-
return "ap-east-1" if hostname =~ /ap-east|hong-kong/
|
123
|
-
return "ap-south-1" if hostname =~ /ap-south|mumbai/
|
124
|
-
return "ap-northeast-1" if hostname =~ /ap-northeast-1|tokyo/
|
125
|
-
return "ap-northeast-2" if hostname =~ /ap-northeast-2|seoul/
|
126
|
-
return "ap-northeast-3" if hostname =~ /ap-northeast-3|osaka/
|
127
|
-
return "ap-southeast-1" if hostname =~ /ap-southeast-1|singapore/
|
128
|
-
return "ap-southeast-2" if hostname =~ /ap-southeast-2|sydney/
|
129
|
-
return "ca-central-1" if hostname =~ /ca-central|canada|montreal/
|
130
|
-
return "eu-central-1" if hostname =~ /eu-central-1|frankfurt/
|
131
|
-
return "eu-west-1" if hostname =~ /eu-west-1|ireland|dublin/
|
132
|
-
return "eu-west-2" if hostname =~ /eu-west-2|london/
|
133
|
-
return "eu-west-3" if hostname =~ /eu-west-3|paris/
|
134
|
-
return "eu-north-1" if hostname =~ /eu-north-1|stockholm/
|
135
|
-
return "eu-south-1" if hostname =~ /eu-south-1|milan/
|
136
|
-
return "me-south-1" if hostname =~ /me-south-1|bahrain/
|
137
|
-
return "sa-east-1" if hostname =~ /sa-east-1|sao-paulo/
|
138
|
-
end
|
139
|
-
|
140
|
-
# Azure regions from hostname
|
141
|
-
if hostname =~ /azure|windows\.net|cloudapp/
|
142
|
-
return "eastus" if hostname =~ /eastus|virginia/
|
143
|
-
return "eastus2" if hostname =~ /eastus2/
|
144
|
-
return "centralus" if hostname =~ /centralus|iowa/
|
145
|
-
return "northcentralus" if hostname =~ /northcentralus|illinois/
|
146
|
-
return "southcentralus" if hostname =~ /southcentralus|texas/
|
147
|
-
return "westus" if hostname =~ /westus|california/
|
148
|
-
return "westus2" if hostname =~ /westus2|washington/
|
149
|
-
return "westus3" if hostname =~ /westus3|phoenix/
|
150
|
-
return "australiaeast" if hostname =~ /australiaeast|sydney/
|
151
|
-
return "brazilsouth" if hostname =~ /brazilsouth|sao-paulo/
|
152
|
-
return "canadacentral" if hostname =~ /canadacentral|toronto/
|
153
|
-
return "centralindia" if hostname =~ /centralindia|pune/
|
154
|
-
return "eastasia" if hostname =~ /eastasia|hong-kong/
|
155
|
-
return "francecentral" if hostname =~ /francecentral|paris/
|
156
|
-
return "germanywestcentral" if hostname =~ /germanywestcentral|frankfurt/
|
157
|
-
return "japaneast" if hostname =~ /japaneast|tokyo/
|
158
|
-
return "koreacentral" if hostname =~ /koreacentral|seoul/
|
159
|
-
return "northeurope" if hostname =~ /northeurope|ireland/
|
160
|
-
return "southeastasia" if hostname =~ /southeastasia|singapore/
|
161
|
-
return "southindia" if hostname =~ /southindia|chennai/
|
162
|
-
return "swedencentral" if hostname =~ /swedencentral|stockholm/
|
163
|
-
return "switzerlandnorth" if hostname =~ /switzerlandnorth|zurich/
|
164
|
-
return "uksouth" if hostname =~ /uksouth|london/
|
165
|
-
return "westeurope" if hostname =~ /westeurope|netherlands/
|
166
|
-
end
|
167
|
-
|
168
|
-
# Google Cloud regions from hostname
|
169
|
-
if hostname =~ /google|googlecloud|gcp|appspot/
|
170
|
-
return "us-central1" if hostname =~ /us-central1|iowa/
|
171
|
-
return "us-east1" if hostname =~ /us-east1|south-carolina/
|
172
|
-
return "us-east4" if hostname =~ /us-east4|virginia/
|
173
|
-
return "us-west1" if hostname =~ /us-west1|oregon/
|
174
|
-
return "us-west2" if hostname =~ /us-west2|los-angeles/
|
175
|
-
return "us-west3" if hostname =~ /us-west3|salt-lake-city/
|
176
|
-
return "us-west4" if hostname =~ /us-west4|las-vegas/
|
177
|
-
return "northamerica-northeast1" if hostname =~ /northamerica-northeast1|montreal/
|
178
|
-
return "southamerica-east1" if hostname =~ /southamerica-east1|sao-paulo/
|
179
|
-
return "europe-west1" if hostname =~ /europe-west1|belgium/
|
180
|
-
return "europe-west2" if hostname =~ /europe-west2|london/
|
181
|
-
return "europe-west3" if hostname =~ /europe-west3|frankfurt/
|
182
|
-
return "europe-west4" if hostname =~ /europe-west4|netherlands/
|
183
|
-
return "europe-west6" if hostname =~ /europe-west6|zurich/
|
184
|
-
return "europe-north1" if hostname =~ /europe-north1|finland/
|
185
|
-
return "asia-east1" if hostname =~ /asia-east1|taiwan/
|
186
|
-
return "asia-east2" if hostname =~ /asia-east2|hong-kong/
|
187
|
-
return "asia-northeast1" if hostname =~ /asia-northeast1|tokyo/
|
188
|
-
return "asia-northeast2" if hostname =~ /asia-northeast2|osaka/
|
189
|
-
return "asia-northeast3" if hostname =~ /asia-northeast3|seoul/
|
190
|
-
return "asia-south1" if hostname =~ /asia-south1|mumbai/
|
191
|
-
return "asia-southeast1" if hostname =~ /asia-southeast1|singapore/
|
192
|
-
return "asia-southeast2" if hostname =~ /asia-southeast2|jakarta/
|
193
|
-
return "australia-southeast1" if hostname =~ /australia-southeast1|sydney/
|
194
|
-
end
|
195
|
-
|
196
|
-
# Fallback to IP-based lookup via Geocoder
|
197
|
-
return nil unless geo
|
198
|
-
|
199
|
-
# City-based detection for common cloud cities
|
200
|
-
city = geo&.data&.dig('city')&.downcase
|
201
|
-
if city
|
202
|
-
case city
|
203
|
-
when 'ashburn', 'sterling', 'herndon', 'chantilly'
|
204
|
-
return "us-east-1" # AWS us-east-1 or equivalent
|
205
|
-
when 'columbus', 'dublin', 'hilliard'
|
206
|
-
return "us-east-2" # AWS us-east-2
|
207
|
-
when 'san jose', 'santa clara', 'milpitas', 'fremont'
|
208
|
-
return "us-west-1" # AWS us-west-1
|
209
|
-
when 'portland', 'hillsboro', 'prineville', 'the dalles'
|
210
|
-
return "us-west-2" # AWS us-west-2 / GCP us-west1
|
211
|
-
when 'phoenix', 'tempe', 'mesa'
|
212
|
-
return "westus3" # Azure westus3
|
213
|
-
when 'dallas', 'fort worth', 'san antonio'
|
214
|
-
return "southcentralus" # Azure southcentralus
|
215
|
-
when 'montreal', 'beauharnois', 'quebec'
|
216
|
-
return "ca-central-1" # AWS ca-central-1
|
217
|
-
when 'toronto'
|
218
|
-
return "canadacentral" # Azure canadacentral
|
219
|
-
when 'frankfurt', 'munich'
|
220
|
-
return "eu-central-1" # AWS eu-central-1
|
221
|
-
when 'london'
|
222
|
-
return "eu-west-2" # AWS eu-west-2
|
223
|
-
when 'paris'
|
224
|
-
return "eu-west-3" # AWS eu-west-3
|
225
|
-
when 'dublin', 'clondalkin'
|
226
|
-
return "eu-west-1" # AWS eu-west-1
|
227
|
-
when 'stockholm'
|
228
|
-
return "eu-north-1" # AWS eu-north-1
|
229
|
-
when 'milan'
|
230
|
-
return "eu-south-1" # AWS eu-south-1
|
231
|
-
when 'sydney', 'melbourne'
|
232
|
-
return "ap-southeast-2" # AWS ap-southeast-2
|
233
|
-
when 'singapore'
|
234
|
-
return "ap-southeast-1" # AWS ap-southeast-1
|
235
|
-
when 'tokyo', 'osaka'
|
236
|
-
return "ap-northeast-1" # AWS ap-northeast-1
|
237
|
-
when 'seoul'
|
238
|
-
return "ap-northeast-2" # AWS ap-northeast-2
|
239
|
-
when 'mumbai'
|
240
|
-
return "ap-south-1" # AWS ap-south-1
|
241
|
-
when 'hong kong'
|
242
|
-
return "ap-east-1" # AWS ap-east-1
|
243
|
-
when 'são paulo', 'sao paulo'
|
244
|
-
return "sa-east-1" # AWS sa-east-1
|
245
|
-
end
|
246
|
-
end
|
247
|
-
|
248
|
-
# Region/State-based mapping to approximate cloud region
|
249
|
-
region_name = geo&.data&.dig('region')
|
250
|
-
country = geo&.data&.dig('country')
|
251
|
-
|
252
|
-
if country == 'United States'
|
253
|
-
case region_name
|
254
|
-
when 'Virginia', 'Maryland', 'District of Columbia'
|
255
|
-
return "us-east-1"
|
256
|
-
when 'Ohio', 'Indiana', 'Michigan'
|
257
|
-
return "us-east-2"
|
258
|
-
when 'California'
|
259
|
-
return "us-west-1"
|
260
|
-
when 'Oregon', 'Washington', 'Idaho'
|
261
|
-
return "us-west-2"
|
262
|
-
when 'Nevada', 'Utah', 'Arizona'
|
263
|
-
return "us-west-2"
|
264
|
-
when 'Texas', 'Oklahoma', 'Louisiana'
|
265
|
-
return "southcentralus"
|
266
|
-
when 'Illinois', 'Iowa', 'Minnesota', 'Missouri', 'Wisconsin'
|
267
|
-
return "us-central1"
|
268
|
-
end
|
269
|
-
elsif country == 'Canada'
|
270
|
-
case region_name
|
271
|
-
when 'Quebec'
|
272
|
-
return "ca-central-1"
|
273
|
-
when 'Ontario'
|
274
|
-
return "canadacentral"
|
275
|
-
end
|
276
|
-
elsif country == 'Brazil'
|
277
|
-
return "sa-east-1"
|
278
|
-
elsif country == 'Ireland'
|
279
|
-
return "eu-west-1"
|
280
|
-
elsif country == 'United Kingdom'
|
281
|
-
return "eu-west-2"
|
282
|
-
elsif country == 'France'
|
283
|
-
return "eu-west-3"
|
284
|
-
elsif country == 'Germany'
|
285
|
-
return "eu-central-1"
|
286
|
-
elsif country == 'Sweden' || country == 'Norway' || country == 'Finland'
|
287
|
-
return "eu-north-1"
|
288
|
-
elsif country == 'Italy'
|
289
|
-
return "eu-south-1"
|
290
|
-
elsif country == 'India'
|
291
|
-
return "ap-south-1"
|
292
|
-
elsif country == 'Singapore'
|
293
|
-
return "ap-southeast-1"
|
294
|
-
elsif country == 'Australia'
|
295
|
-
return "ap-southeast-2"
|
296
|
-
elsif country == 'Japan'
|
297
|
-
return "ap-northeast-1"
|
298
|
-
elsif country == 'South Korea'
|
299
|
-
return "ap-northeast-2"
|
300
|
-
elsif country == 'Hong Kong'
|
301
|
-
return "ap-east-1"
|
302
|
-
end
|
303
|
-
|
304
|
-
# Final fallback
|
305
|
-
region_name || geo&.data&.dig('country_code')&.downcase
|
113
|
+
Region.detect_region(host, geo)
|
306
114
|
end
|
307
115
|
|
308
116
|
def asn
|
@@ -357,7 +165,16 @@ module Detector
|
|
357
165
|
end
|
358
166
|
|
359
167
|
def table_count
|
360
|
-
nil
|
168
|
+
return nil unless valid? && tables? && connection?
|
169
|
+
|
170
|
+
count = 0
|
171
|
+
database_list = databases
|
172
|
+
|
173
|
+
database_list.each do |db|
|
174
|
+
count += tables(db).size
|
175
|
+
end
|
176
|
+
|
177
|
+
count.zero? ? nil : count
|
361
178
|
end
|
362
179
|
|
363
180
|
def database_count
|
@@ -392,78 +209,15 @@ module Detector
|
|
392
209
|
def infrastructure
|
393
210
|
return nil unless valid?
|
394
211
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
"Build.io"
|
405
|
-
when /heroku/, /herokuapp/
|
406
|
-
"Heroku"
|
407
|
-
when /digitalocean/, /droplet/
|
408
|
-
"DigitalOcean"
|
409
|
-
when /linode/, /linodeobjects/
|
410
|
-
"Linode"
|
411
|
-
when /vultr/
|
412
|
-
"Vultr"
|
413
|
-
when /netlify/
|
414
|
-
"Netlify"
|
415
|
-
when /vercel/, /zeit\.co/, /now\.sh/
|
416
|
-
"Vercel"
|
417
|
-
when /github\.io/, /githubusercontent/, /github\.dev/
|
418
|
-
"GitHub"
|
419
|
-
when /gitlab\.io/, /gitlab-static/
|
420
|
-
"GitLab"
|
421
|
-
when /oracle/, /oraclecloud/
|
422
|
-
"Oracle Cloud"
|
423
|
-
when /ibm/, /bluemix/, /ibmcloud/
|
424
|
-
"IBM Cloud"
|
425
|
-
when /cloudflare/, /workers\.dev/
|
426
|
-
"Cloudflare"
|
427
|
-
when /fastly/
|
428
|
-
"Fastly"
|
429
|
-
when /akamai/
|
430
|
-
"Akamai"
|
431
|
-
when /render\.com/
|
432
|
-
"Render"
|
433
|
-
when /fly\.io/
|
434
|
-
"Fly.io"
|
435
|
-
when /railway\.app/
|
436
|
-
"Railway"
|
437
|
-
when /upcloud/
|
438
|
-
"UpCloud"
|
439
|
-
when /hetzner/
|
440
|
-
"Hetzner"
|
441
|
-
when /ovh/, /ovhcloud/
|
442
|
-
"OVH"
|
443
|
-
when /scaleway/
|
444
|
-
"Scaleway"
|
445
|
-
when /contabo/
|
446
|
-
"Contabo"
|
447
|
-
when /dreamhost/
|
448
|
-
"DreamHost"
|
449
|
-
when /hostgator/
|
450
|
-
"HostGator"
|
451
|
-
when /bluehost/
|
452
|
-
"Bluehost"
|
453
|
-
when /siteground/
|
454
|
-
"SiteGround"
|
455
|
-
when /namecheap/
|
456
|
-
"Namecheap"
|
457
|
-
when /godaddy/
|
458
|
-
"GoDaddy"
|
459
|
-
when /ionos/
|
460
|
-
"IONOS"
|
461
|
-
when /hostinger/
|
462
|
-
"Hostinger"
|
463
|
-
else
|
464
|
-
# If geo data available, return organization, otherwise nil
|
465
|
-
geo&.data&.dig('org')
|
466
|
-
end
|
212
|
+
provider = Vendor.detect_provider(host)
|
213
|
+
return provider if provider
|
214
|
+
|
215
|
+
# If geo data available, return organization, otherwise nil
|
216
|
+
geo&.data&.dig('org')
|
217
|
+
end
|
218
|
+
|
219
|
+
def replication_available?
|
220
|
+
nil
|
467
221
|
end
|
468
222
|
end
|
469
223
|
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
module Detector
|
2
|
+
class Region
|
3
|
+
class << self
|
4
|
+
def detect_region(host, geo)
|
5
|
+
return nil unless host || geo
|
6
|
+
|
7
|
+
hostname = host.to_s.downcase
|
8
|
+
|
9
|
+
# Try to determine region from hostname first
|
10
|
+
aws_region = detect_aws_region(hostname)
|
11
|
+
return aws_region if aws_region
|
12
|
+
|
13
|
+
azure_region = detect_azure_region(hostname)
|
14
|
+
return azure_region if azure_region
|
15
|
+
|
16
|
+
gcp_region = detect_gcp_region(hostname)
|
17
|
+
return gcp_region if gcp_region
|
18
|
+
|
19
|
+
# Fallbacks to geocoder data
|
20
|
+
return nil unless geo
|
21
|
+
|
22
|
+
# Try city-based detection
|
23
|
+
city_region = detect_region_by_city(geo&.data&.dig('city')&.downcase)
|
24
|
+
return city_region if city_region
|
25
|
+
|
26
|
+
# Try region/country based detection
|
27
|
+
geo_region = detect_region_by_geography(geo&.data&.dig('region'), geo&.data&.dig('country'))
|
28
|
+
return geo_region if geo_region
|
29
|
+
|
30
|
+
# Final fallback
|
31
|
+
geo&.data&.dig('region') || geo&.data&.dig('country_code')&.downcase
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def detect_aws_region(hostname)
|
37
|
+
return nil unless hostname =~ /amazonaws\.com/ || hostname =~ /aws/
|
38
|
+
|
39
|
+
return "us-east-1" if hostname =~ /us-east-1|virginia|nova/
|
40
|
+
return "us-east-2" if hostname =~ /us-east-2|ohio/
|
41
|
+
return "us-west-1" if hostname =~ /us-west-1|california|norcal/
|
42
|
+
return "us-west-2" if hostname =~ /us-west-2|oregon/
|
43
|
+
return "af-south-1" if hostname =~ /af-south|cape-town/
|
44
|
+
return "ap-east-1" if hostname =~ /ap-east|hong-kong/
|
45
|
+
return "ap-south-1" if hostname =~ /ap-south|mumbai/
|
46
|
+
return "ap-northeast-1" if hostname =~ /ap-northeast-1|tokyo/
|
47
|
+
return "ap-northeast-2" if hostname =~ /ap-northeast-2|seoul/
|
48
|
+
return "ap-northeast-3" if hostname =~ /ap-northeast-3|osaka/
|
49
|
+
return "ap-southeast-1" if hostname =~ /ap-southeast-1|singapore/
|
50
|
+
return "ap-southeast-2" if hostname =~ /ap-southeast-2|sydney/
|
51
|
+
return "ca-central-1" if hostname =~ /ca-central|canada|montreal/
|
52
|
+
return "eu-central-1" if hostname =~ /eu-central-1|frankfurt/
|
53
|
+
return "eu-west-1" if hostname =~ /eu-west-1|ireland|dublin/
|
54
|
+
return "eu-west-2" if hostname =~ /eu-west-2|london/
|
55
|
+
return "eu-west-3" if hostname =~ /eu-west-3|paris/
|
56
|
+
return "eu-north-1" if hostname =~ /eu-north-1|stockholm/
|
57
|
+
return "eu-south-1" if hostname =~ /eu-south-1|milan/
|
58
|
+
return "me-south-1" if hostname =~ /me-south-1|bahrain/
|
59
|
+
return "sa-east-1" if hostname =~ /sa-east-1|sao-paulo/
|
60
|
+
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def detect_azure_region(hostname)
|
65
|
+
return nil unless hostname =~ /azure|windows\.net|cloudapp/
|
66
|
+
|
67
|
+
return "eastus" if hostname =~ /eastus|virginia/
|
68
|
+
return "eastus2" if hostname =~ /eastus2/
|
69
|
+
return "centralus" if hostname =~ /centralus|iowa/
|
70
|
+
return "northcentralus" if hostname =~ /northcentralus|illinois/
|
71
|
+
return "southcentralus" if hostname =~ /southcentralus|texas/
|
72
|
+
return "westus" if hostname =~ /westus|california/
|
73
|
+
return "westus2" if hostname =~ /westus2|washington/
|
74
|
+
return "westus3" if hostname =~ /westus3|phoenix/
|
75
|
+
return "australiaeast" if hostname =~ /australiaeast|sydney/
|
76
|
+
return "brazilsouth" if hostname =~ /brazilsouth|sao-paulo/
|
77
|
+
return "canadacentral" if hostname =~ /canadacentral|toronto/
|
78
|
+
return "centralindia" if hostname =~ /centralindia|pune/
|
79
|
+
return "eastasia" if hostname =~ /eastasia|hong-kong/
|
80
|
+
return "francecentral" if hostname =~ /francecentral|paris/
|
81
|
+
return "germanywestcentral" if hostname =~ /germanywestcentral|frankfurt/
|
82
|
+
return "japaneast" if hostname =~ /japaneast|tokyo/
|
83
|
+
return "koreacentral" if hostname =~ /koreacentral|seoul/
|
84
|
+
return "northeurope" if hostname =~ /northeurope|ireland/
|
85
|
+
return "southeastasia" if hostname =~ /southeastasia|singapore/
|
86
|
+
return "southindia" if hostname =~ /southindia|chennai/
|
87
|
+
return "swedencentral" if hostname =~ /swedencentral|stockholm/
|
88
|
+
return "switzerlandnorth" if hostname =~ /switzerlandnorth|zurich/
|
89
|
+
return "uksouth" if hostname =~ /uksouth|london/
|
90
|
+
return "westeurope" if hostname =~ /westeurope|netherlands/
|
91
|
+
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
|
95
|
+
def detect_gcp_region(hostname)
|
96
|
+
return nil unless hostname =~ /google|googlecloud|gcp|appspot/
|
97
|
+
|
98
|
+
return "us-central1" if hostname =~ /us-central1|iowa/
|
99
|
+
return "us-east1" if hostname =~ /us-east1|south-carolina/
|
100
|
+
return "us-east4" if hostname =~ /us-east4|virginia/
|
101
|
+
return "us-west1" if hostname =~ /us-west1|oregon/
|
102
|
+
return "us-west2" if hostname =~ /us-west2|los-angeles/
|
103
|
+
return "us-west3" if hostname =~ /us-west3|salt-lake-city/
|
104
|
+
return "us-west4" if hostname =~ /us-west4|las-vegas/
|
105
|
+
return "northamerica-northeast1" if hostname =~ /northamerica-northeast1|montreal/
|
106
|
+
return "southamerica-east1" if hostname =~ /southamerica-east1|sao-paulo/
|
107
|
+
return "europe-west1" if hostname =~ /europe-west1|belgium/
|
108
|
+
return "europe-west2" if hostname =~ /europe-west2|london/
|
109
|
+
return "europe-west3" if hostname =~ /europe-west3|frankfurt/
|
110
|
+
return "europe-west4" if hostname =~ /europe-west4|netherlands/
|
111
|
+
return "europe-west6" if hostname =~ /europe-west6|zurich/
|
112
|
+
return "europe-north1" if hostname =~ /europe-north1|finland/
|
113
|
+
return "asia-east1" if hostname =~ /asia-east1|taiwan/
|
114
|
+
return "asia-east2" if hostname =~ /asia-east2|hong-kong/
|
115
|
+
return "asia-northeast1" if hostname =~ /asia-northeast1|tokyo/
|
116
|
+
return "asia-northeast2" if hostname =~ /asia-northeast2|osaka/
|
117
|
+
return "asia-northeast3" if hostname =~ /asia-northeast3|seoul/
|
118
|
+
return "asia-south1" if hostname =~ /asia-south1|mumbai/
|
119
|
+
return "asia-southeast1" if hostname =~ /asia-southeast1|singapore/
|
120
|
+
return "asia-southeast2" if hostname =~ /asia-southeast2|jakarta/
|
121
|
+
return "australia-southeast1" if hostname =~ /australia-southeast1|sydney/
|
122
|
+
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
|
126
|
+
def detect_region_by_city(city)
|
127
|
+
return nil unless city
|
128
|
+
|
129
|
+
case city
|
130
|
+
when 'ashburn', 'sterling', 'herndon', 'chantilly'
|
131
|
+
"us-east-1" # AWS us-east-1 or equivalent
|
132
|
+
when 'columbus', 'dublin', 'hilliard'
|
133
|
+
"us-east-2" # AWS us-east-2
|
134
|
+
when 'san jose', 'santa clara', 'milpitas', 'fremont'
|
135
|
+
"us-west-1" # AWS us-west-1
|
136
|
+
when 'portland', 'hillsboro', 'prineville', 'the dalles'
|
137
|
+
"us-west-2" # AWS us-west-2 / GCP us-west1
|
138
|
+
when 'phoenix', 'tempe', 'mesa'
|
139
|
+
"westus3" # Azure westus3
|
140
|
+
when 'dallas', 'fort worth', 'san antonio'
|
141
|
+
"southcentralus" # Azure southcentralus
|
142
|
+
when 'montreal', 'beauharnois', 'quebec'
|
143
|
+
"ca-central-1" # AWS ca-central-1
|
144
|
+
when 'toronto'
|
145
|
+
"canadacentral" # Azure canadacentral
|
146
|
+
when 'frankfurt', 'munich'
|
147
|
+
"eu-central-1" # AWS eu-central-1
|
148
|
+
when 'london'
|
149
|
+
"eu-west-2" # AWS eu-west-2
|
150
|
+
when 'paris'
|
151
|
+
"eu-west-3" # AWS eu-west-3
|
152
|
+
when 'dublin', 'clondalkin'
|
153
|
+
"eu-west-1" # AWS eu-west-1
|
154
|
+
when 'stockholm'
|
155
|
+
"eu-north-1" # AWS eu-north-1
|
156
|
+
when 'milan'
|
157
|
+
"eu-south-1" # AWS eu-south-1
|
158
|
+
when 'sydney', 'melbourne'
|
159
|
+
"ap-southeast-2" # AWS ap-southeast-2
|
160
|
+
when 'singapore'
|
161
|
+
"ap-southeast-1" # AWS ap-southeast-1
|
162
|
+
when 'tokyo', 'osaka'
|
163
|
+
"ap-northeast-1" # AWS ap-northeast-1
|
164
|
+
when 'seoul'
|
165
|
+
"ap-northeast-2" # AWS ap-northeast-2
|
166
|
+
when 'mumbai'
|
167
|
+
"ap-south-1" # AWS ap-south-1
|
168
|
+
when 'hong kong'
|
169
|
+
"ap-east-1" # AWS ap-east-1
|
170
|
+
when 'são paulo', 'sao paulo'
|
171
|
+
"sa-east-1" # AWS sa-east-1
|
172
|
+
else
|
173
|
+
nil
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def detect_region_by_geography(region_name, country)
|
178
|
+
return nil unless region_name || country
|
179
|
+
|
180
|
+
if country == 'United States'
|
181
|
+
case region_name
|
182
|
+
when 'Virginia', 'Maryland', 'District of Columbia'
|
183
|
+
"us-east-1"
|
184
|
+
when 'Ohio', 'Indiana', 'Michigan'
|
185
|
+
"us-east-2"
|
186
|
+
when 'California'
|
187
|
+
"us-west-1"
|
188
|
+
when 'Oregon', 'Washington', 'Idaho'
|
189
|
+
"us-west-2"
|
190
|
+
when 'Nevada', 'Utah', 'Arizona'
|
191
|
+
"us-west-2"
|
192
|
+
when 'Texas', 'Oklahoma', 'Louisiana'
|
193
|
+
"southcentralus"
|
194
|
+
when 'Illinois', 'Iowa', 'Minnesota', 'Missouri', 'Wisconsin'
|
195
|
+
"us-central1"
|
196
|
+
else
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
elsif country == 'Canada'
|
200
|
+
case region_name
|
201
|
+
when 'Quebec'
|
202
|
+
"ca-central-1"
|
203
|
+
when 'Ontario'
|
204
|
+
"canadacentral"
|
205
|
+
else
|
206
|
+
nil
|
207
|
+
end
|
208
|
+
elsif country == 'Brazil'
|
209
|
+
"sa-east-1"
|
210
|
+
elsif country == 'Ireland'
|
211
|
+
"eu-west-1"
|
212
|
+
elsif country == 'United Kingdom'
|
213
|
+
"eu-west-2"
|
214
|
+
elsif country == 'France'
|
215
|
+
"eu-west-3"
|
216
|
+
elsif country == 'Germany'
|
217
|
+
"eu-central-1"
|
218
|
+
elsif country == 'Sweden' || country == 'Norway' || country == 'Finland'
|
219
|
+
"eu-north-1"
|
220
|
+
elsif country == 'Italy'
|
221
|
+
"eu-south-1"
|
222
|
+
elsif country == 'India'
|
223
|
+
"ap-south-1"
|
224
|
+
elsif country == 'Singapore'
|
225
|
+
"ap-southeast-1"
|
226
|
+
elsif country == 'Australia'
|
227
|
+
"ap-southeast-2"
|
228
|
+
elsif country == 'Japan'
|
229
|
+
"ap-northeast-1"
|
230
|
+
elsif country == 'South Korea'
|
231
|
+
"ap-northeast-2"
|
232
|
+
elsif country == 'Hong Kong'
|
233
|
+
"ap-east-1"
|
234
|
+
else
|
235
|
+
nil
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Detector
|
2
|
+
class Vendor
|
3
|
+
class << self
|
4
|
+
def detect_provider(hostname)
|
5
|
+
return nil unless hostname
|
6
|
+
|
7
|
+
hostname = hostname.to_s.downcase
|
8
|
+
|
9
|
+
case hostname
|
10
|
+
when /amazon/, /aws/, /amazonaws/, /ec2/, /s3/, /dynamodb/, /rds\./, /elasticbeanstalk/
|
11
|
+
"Amazon Web Services"
|
12
|
+
when /google/, /googlecloud/, /appspot/, /gcp/, /compute\./, /cloud\.g/
|
13
|
+
"Google Cloud Platform"
|
14
|
+
when /azure/, /azurewebsites/, /cloudapp\./, /windows\.net/
|
15
|
+
"Microsoft Azure"
|
16
|
+
when /antimony/
|
17
|
+
"Build.io"
|
18
|
+
when /heroku/, /herokuapp/
|
19
|
+
"Heroku"
|
20
|
+
when /digitalocean/, /droplet/
|
21
|
+
"DigitalOcean"
|
22
|
+
when /linode/, /linodeobjects/
|
23
|
+
"Linode"
|
24
|
+
when /vultr/
|
25
|
+
"Vultr"
|
26
|
+
when /netlify/
|
27
|
+
"Netlify"
|
28
|
+
when /vercel/, /zeit\.co/, /now\.sh/
|
29
|
+
"Vercel"
|
30
|
+
when /github\.io/, /githubusercontent/, /github\.dev/
|
31
|
+
"GitHub"
|
32
|
+
when /gitlab\.io/, /gitlab-static/
|
33
|
+
"GitLab"
|
34
|
+
when /oracle/, /oraclecloud/
|
35
|
+
"Oracle Cloud"
|
36
|
+
when /ibm/, /bluemix/, /ibmcloud/
|
37
|
+
"IBM Cloud"
|
38
|
+
when /cloudflare/, /workers\.dev/
|
39
|
+
"Cloudflare"
|
40
|
+
when /fastly/
|
41
|
+
"Fastly"
|
42
|
+
when /akamai/
|
43
|
+
"Akamai"
|
44
|
+
when /render\.com/
|
45
|
+
"Render"
|
46
|
+
when /fly\.io/
|
47
|
+
"Fly.io"
|
48
|
+
when /railway\.app/
|
49
|
+
"Railway"
|
50
|
+
when /upcloud/
|
51
|
+
"UpCloud"
|
52
|
+
when /hetzner/
|
53
|
+
"Hetzner"
|
54
|
+
when /ovh/, /ovhcloud/
|
55
|
+
"OVH"
|
56
|
+
when /scaleway/
|
57
|
+
"Scaleway"
|
58
|
+
when /contabo/
|
59
|
+
"Contabo"
|
60
|
+
when /dreamhost/
|
61
|
+
"DreamHost"
|
62
|
+
when /hostgator/
|
63
|
+
"HostGator"
|
64
|
+
when /bluehost/
|
65
|
+
"Bluehost"
|
66
|
+
when /siteground/
|
67
|
+
"SiteGround"
|
68
|
+
when /namecheap/
|
69
|
+
"Namecheap"
|
70
|
+
when /godaddy/
|
71
|
+
"GoDaddy"
|
72
|
+
when /ionos/
|
73
|
+
"IONOS"
|
74
|
+
when /hostinger/
|
75
|
+
"Hostinger"
|
76
|
+
else
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/detector/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: detector
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonathan Siegel
|
@@ -183,6 +183,8 @@ files:
|
|
183
183
|
- lib/detector/addons/redis.rb
|
184
184
|
- lib/detector/addons/smtp.rb
|
185
185
|
- lib/detector/base.rb
|
186
|
+
- lib/detector/region.rb
|
187
|
+
- lib/detector/vendor.rb
|
186
188
|
- lib/detector/version.rb
|
187
189
|
homepage: https://github.com/usiegj00/detector
|
188
190
|
licenses:
|