detector 0.2.1 → 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 +41 -4
- data/lib/detector/addons/postgres.rb +56 -6
- data/lib/detector/addons/redis.rb +24 -0
- data/lib/detector/addons/smtp.rb +4 -0
- data/lib/detector/base.rb +53 -270
- 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
|
|
@@ -90,6 +97,10 @@ module Detector
|
|
90
97
|
"mysql"
|
91
98
|
end
|
92
99
|
|
100
|
+
def protocol_type
|
101
|
+
:tcp
|
102
|
+
end
|
103
|
+
|
93
104
|
def user_access_level
|
94
105
|
return nil unless connection
|
95
106
|
|
@@ -150,6 +161,32 @@ module Detector
|
|
150
161
|
"Limited access"
|
151
162
|
end
|
152
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
|
153
190
|
end
|
154
191
|
end
|
155
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
|
@@ -115,6 +150,10 @@ module Detector
|
|
115
150
|
"psql"
|
116
151
|
end
|
117
152
|
|
153
|
+
def protocol_type
|
154
|
+
:tcp
|
155
|
+
end
|
156
|
+
|
118
157
|
def user_access_level
|
119
158
|
return nil unless connection
|
120
159
|
|
@@ -154,6 +193,17 @@ module Detector
|
|
154
193
|
end
|
155
194
|
end
|
156
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
|
157
207
|
end
|
158
208
|
end
|
159
209
|
|
@@ -72,6 +72,10 @@ module Detector
|
|
72
72
|
"redis-cli"
|
73
73
|
end
|
74
74
|
|
75
|
+
def protocol_type
|
76
|
+
:tcp
|
77
|
+
end
|
78
|
+
|
75
79
|
def user_access_level
|
76
80
|
return nil unless connection
|
77
81
|
|
@@ -147,6 +151,26 @@ module Detector
|
|
147
151
|
end
|
148
152
|
end
|
149
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
|
150
174
|
end
|
151
175
|
end
|
152
176
|
|
data/lib/detector/addons/smtp.rb
CHANGED
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
|
@@ -320,15 +128,53 @@ module Detector
|
|
320
128
|
|
321
129
|
def ping
|
322
130
|
return nil unless valid?
|
323
|
-
|
131
|
+
transport?
|
132
|
+
end
|
133
|
+
|
134
|
+
def transport?
|
135
|
+
protocol_type == :tcp ? tcp_test : udp_test
|
136
|
+
end
|
137
|
+
|
138
|
+
# Should be implemented by subclasses
|
139
|
+
def protocol_type
|
140
|
+
:tcp # Default to TCP
|
324
141
|
end
|
325
142
|
|
326
143
|
def tcp_test
|
327
|
-
|
144
|
+
return nil unless ip && port
|
145
|
+
begin
|
146
|
+
socket = TCPSocket.new(ip, port)
|
147
|
+
socket.close
|
148
|
+
true
|
149
|
+
rescue => e
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def udp_test
|
155
|
+
return nil unless ip && port
|
156
|
+
begin
|
157
|
+
socket = UDPSocket.new
|
158
|
+
socket.connect(ip, port)
|
159
|
+
socket.send("", 0) # Send empty packet as probe
|
160
|
+
socket.close
|
161
|
+
true
|
162
|
+
rescue => e
|
163
|
+
nil
|
164
|
+
end
|
328
165
|
end
|
329
166
|
|
330
167
|
def table_count
|
331
|
-
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
|
332
178
|
end
|
333
179
|
|
334
180
|
def database_count
|
@@ -363,78 +209,15 @@ module Detector
|
|
363
209
|
def infrastructure
|
364
210
|
return nil unless valid?
|
365
211
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
"Build.io"
|
376
|
-
when /heroku/, /herokuapp/
|
377
|
-
"Heroku"
|
378
|
-
when /digitalocean/, /droplet/
|
379
|
-
"DigitalOcean"
|
380
|
-
when /linode/, /linodeobjects/
|
381
|
-
"Linode"
|
382
|
-
when /vultr/
|
383
|
-
"Vultr"
|
384
|
-
when /netlify/
|
385
|
-
"Netlify"
|
386
|
-
when /vercel/, /zeit\.co/, /now\.sh/
|
387
|
-
"Vercel"
|
388
|
-
when /github\.io/, /githubusercontent/, /github\.dev/
|
389
|
-
"GitHub"
|
390
|
-
when /gitlab\.io/, /gitlab-static/
|
391
|
-
"GitLab"
|
392
|
-
when /oracle/, /oraclecloud/
|
393
|
-
"Oracle Cloud"
|
394
|
-
when /ibm/, /bluemix/, /ibmcloud/
|
395
|
-
"IBM Cloud"
|
396
|
-
when /cloudflare/, /workers\.dev/
|
397
|
-
"Cloudflare"
|
398
|
-
when /fastly/
|
399
|
-
"Fastly"
|
400
|
-
when /akamai/
|
401
|
-
"Akamai"
|
402
|
-
when /render\.com/
|
403
|
-
"Render"
|
404
|
-
when /fly\.io/
|
405
|
-
"Fly.io"
|
406
|
-
when /railway\.app/
|
407
|
-
"Railway"
|
408
|
-
when /upcloud/
|
409
|
-
"UpCloud"
|
410
|
-
when /hetzner/
|
411
|
-
"Hetzner"
|
412
|
-
when /ovh/, /ovhcloud/
|
413
|
-
"OVH"
|
414
|
-
when /scaleway/
|
415
|
-
"Scaleway"
|
416
|
-
when /contabo/
|
417
|
-
"Contabo"
|
418
|
-
when /dreamhost/
|
419
|
-
"DreamHost"
|
420
|
-
when /hostgator/
|
421
|
-
"HostGator"
|
422
|
-
when /bluehost/
|
423
|
-
"Bluehost"
|
424
|
-
when /siteground/
|
425
|
-
"SiteGround"
|
426
|
-
when /namecheap/
|
427
|
-
"Namecheap"
|
428
|
-
when /godaddy/
|
429
|
-
"GoDaddy"
|
430
|
-
when /ionos/
|
431
|
-
"IONOS"
|
432
|
-
when /hostinger/
|
433
|
-
"Hostinger"
|
434
|
-
else
|
435
|
-
# If geo data available, return organization, otherwise nil
|
436
|
-
geo&.data&.dig('org')
|
437
|
-
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
|
438
221
|
end
|
439
222
|
end
|
440
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:
|