cloud_sql_ruby_connector 1.0.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 +7 -0
- data/CHANGELOG.md +22 -0
- data/CODE_OF_CONDUCT.md +43 -0
- data/CONTRIBUTING.md +49 -0
- data/LICENSE +202 -0
- data/README.md +233 -0
- data/SECURITY.md +11 -0
- data/lib/cloud_sql_ruby_connector/auth_types.rb +41 -0
- data/lib/cloud_sql_ruby_connector/credentials/base.rb +61 -0
- data/lib/cloud_sql_ruby_connector/credentials/metadata.rb +50 -0
- data/lib/cloud_sql_ruby_connector/credentials/service_account.rb +115 -0
- data/lib/cloud_sql_ruby_connector/credentials/user_credentials.rb +73 -0
- data/lib/cloud_sql_ruby_connector/errors.rb +55 -0
- data/lib/cloud_sql_ruby_connector/ip_address_types.rb +51 -0
- data/lib/cloud_sql_ruby_connector/postgresql/connector.rb +255 -0
- data/lib/cloud_sql_ruby_connector/rails.rb +128 -0
- data/lib/cloud_sql_ruby_connector/sqladmin_fetcher.rb +182 -0
- data/lib/cloud_sql_ruby_connector/ssl_proxy.rb +92 -0
- data/lib/cloud_sql_ruby_connector/version.rb +19 -0
- data/lib/cloud_sql_ruby_connector.rb +59 -0
- metadata +78 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 Martin Milo
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require_relative "../cloud_sql_ruby_connector"
|
|
18
|
+
require "active_record"
|
|
19
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
|
20
|
+
|
|
21
|
+
module CloudSQLRubyConnector
|
|
22
|
+
# Rails integration for Cloud SQL Connector
|
|
23
|
+
#
|
|
24
|
+
# @example Usage in config/initializers/cloud_sql.rb
|
|
25
|
+
# require "cloud_sql_ruby_connector/rails"
|
|
26
|
+
#
|
|
27
|
+
# CloudSQLRubyConnector::Rails.setup!(
|
|
28
|
+
# instance: "project:region:instance",
|
|
29
|
+
# ip_type: :private,
|
|
30
|
+
# auth_type: :iam
|
|
31
|
+
# )
|
|
32
|
+
#
|
|
33
|
+
# @example Then in database.yml
|
|
34
|
+
# production:
|
|
35
|
+
# adapter: cloud_sql_postgresql
|
|
36
|
+
# database: myapp_production
|
|
37
|
+
# username: myuser@project.iam.gserviceaccount.com
|
|
38
|
+
# pool: 5
|
|
39
|
+
#
|
|
40
|
+
module Rails
|
|
41
|
+
class << self
|
|
42
|
+
attr_reader :connector
|
|
43
|
+
|
|
44
|
+
# Set up the Cloud SQL connector for Rails
|
|
45
|
+
#
|
|
46
|
+
# @param instance [String] Cloud SQL instance connection name
|
|
47
|
+
# @param ip_type [String, Symbol] IP address type (default: :public)
|
|
48
|
+
# @param auth_type [String, Symbol] Authentication type (default: :password)
|
|
49
|
+
# @param credentials [Credentials::Base] Optional custom credentials
|
|
50
|
+
def setup!(instance:, ip_type: IpAddressTypes::PUBLIC, auth_type: AuthTypes::PASSWORD, credentials: nil)
|
|
51
|
+
@connector = PostgreSQL::Connector.new(
|
|
52
|
+
instance,
|
|
53
|
+
ip_type: ip_type,
|
|
54
|
+
auth_type: auth_type,
|
|
55
|
+
credentials: credentials
|
|
56
|
+
)
|
|
57
|
+
register_adapter!
|
|
58
|
+
register_shutdown_hook!
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Close the connector and clean up resources
|
|
62
|
+
def shutdown!
|
|
63
|
+
@connector&.close
|
|
64
|
+
@connector = nil
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def register_adapter!
|
|
70
|
+
ActiveRecord::ConnectionAdapters.register(
|
|
71
|
+
"cloud_sql_postgresql",
|
|
72
|
+
"CloudSQLRubyConnector::Rails::CloudSQLPostgreSQLAdapter",
|
|
73
|
+
"cloud_sql_ruby_connector/rails"
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def register_shutdown_hook!
|
|
78
|
+
at_exit { shutdown! }
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Custom adapter that uses CloudSQLRubyConnector for connections
|
|
83
|
+
class CloudSQLPostgreSQLAdapter < ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
|
|
84
|
+
ADAPTER_NAME = "CloudSQL PostgreSQL"
|
|
85
|
+
|
|
86
|
+
# PG connection options that can be passed through to PG.connect
|
|
87
|
+
# See: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-PARAMKEYWORDS
|
|
88
|
+
ALLOWED_PG_OPTIONS = %i[
|
|
89
|
+
application_name
|
|
90
|
+
client_encoding
|
|
91
|
+
connect_timeout
|
|
92
|
+
options
|
|
93
|
+
keepalives
|
|
94
|
+
keepalives_idle
|
|
95
|
+
keepalives_interval
|
|
96
|
+
keepalives_count
|
|
97
|
+
tcp_user_timeout
|
|
98
|
+
target_session_attrs
|
|
99
|
+
].freeze
|
|
100
|
+
|
|
101
|
+
class << self
|
|
102
|
+
# In Rails 7.1+, new_client receives processed conn_params (user, dbname)
|
|
103
|
+
# not the raw config (username, database)
|
|
104
|
+
def new_client(conn_params)
|
|
105
|
+
connector = CloudSQLRubyConnector::Rails.connector
|
|
106
|
+
raise ConfigurationError, "CloudSQLRubyConnector::Rails.setup! not called" unless connector
|
|
107
|
+
|
|
108
|
+
# conn_params already has :user and :dbname (processed by parent's initialize)
|
|
109
|
+
cfg = if conn_params.respond_to?(:symbolize_keys)
|
|
110
|
+
conn_params.symbolize_keys
|
|
111
|
+
else
|
|
112
|
+
conn_params.to_h.transform_keys(&:to_sym)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Only pass through known PG connection options
|
|
116
|
+
extra_options = cfg.slice(*ALLOWED_PG_OPTIONS)
|
|
117
|
+
|
|
118
|
+
connector.connect(
|
|
119
|
+
user: cfg[:user],
|
|
120
|
+
password: cfg[:password],
|
|
121
|
+
dbname: cfg[:dbname],
|
|
122
|
+
**extra_options
|
|
123
|
+
)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 Martin Milo
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require "net/http"
|
|
18
|
+
require "json"
|
|
19
|
+
require "openssl"
|
|
20
|
+
|
|
21
|
+
module CloudSQLRubyConnector
|
|
22
|
+
# Fetches instance metadata and ephemeral certificates from Cloud SQL Admin API
|
|
23
|
+
class SQLAdminFetcher
|
|
24
|
+
API_VERSION = "v1beta4"
|
|
25
|
+
DEFAULT_ENDPOINT = "https://sqladmin.googleapis.com"
|
|
26
|
+
HTTP_TIMEOUT = 30 # seconds
|
|
27
|
+
|
|
28
|
+
def initialize(credentials:, api_endpoint: nil)
|
|
29
|
+
@credentials = credentials
|
|
30
|
+
@api_endpoint = api_endpoint || DEFAULT_ENDPOINT
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Fetch instance metadata including IP addresses and server CA certificate
|
|
34
|
+
# @param project [String] Google Cloud project ID
|
|
35
|
+
# @param region [String] Cloud SQL instance region
|
|
36
|
+
# @param instance [String] Cloud SQL instance name
|
|
37
|
+
# @return [Hash] instance metadata
|
|
38
|
+
def fetch_metadata(project:, region:, instance:)
|
|
39
|
+
token = @credentials.access_token(scope: :admin)
|
|
40
|
+
uri = URI("#{@api_endpoint}/sql/#{API_VERSION}/projects/#{project}/instances/#{instance}/connectSettings")
|
|
41
|
+
|
|
42
|
+
response = http_get(uri, token)
|
|
43
|
+
data = parse_response(response)
|
|
44
|
+
|
|
45
|
+
raise ConfigurationError, "Region mismatch: expected #{region}, got #{data["region"]}" if data["region"] != region
|
|
46
|
+
|
|
47
|
+
ip_addresses = parse_ip_addresses(
|
|
48
|
+
data["ipAddresses"],
|
|
49
|
+
data["dnsName"],
|
|
50
|
+
data["dnsNames"],
|
|
51
|
+
data["pscEnabled"]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
server_ca_cert = data.dig("serverCaCert", "cert")
|
|
55
|
+
raise ConnectionError, "No valid CA certificate found for instance" if server_ca_cert.nil?
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
ip_addresses: ip_addresses,
|
|
59
|
+
server_ca_cert: server_ca_cert,
|
|
60
|
+
database_version: data["databaseVersion"],
|
|
61
|
+
dns_name: data["dnsName"]
|
|
62
|
+
}
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Fetch an ephemeral certificate for client authentication
|
|
66
|
+
# @param project [String] Google Cloud project ID
|
|
67
|
+
# @param instance [String] Cloud SQL instance name
|
|
68
|
+
# @param public_key [String] RSA public key in PEM format
|
|
69
|
+
# @param auth_type [String] Authentication type (PASSWORD or IAM)
|
|
70
|
+
# @return [Hash] certificate data with :cert and :expiration
|
|
71
|
+
def fetch_ephemeral_cert(project:, instance:, public_key:, auth_type:)
|
|
72
|
+
token = @credentials.access_token(scope: :admin)
|
|
73
|
+
uri = URI("#{@api_endpoint}/sql/#{API_VERSION}/projects/#{project}/instances/#{instance}:generateEphemeralCert")
|
|
74
|
+
|
|
75
|
+
body = { "public_key" => public_key }
|
|
76
|
+
|
|
77
|
+
# For IAM auth, include the login token
|
|
78
|
+
if auth_type == AuthTypes::IAM
|
|
79
|
+
login_token = @credentials.access_token(scope: :login)
|
|
80
|
+
body["access_token"] = login_token
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
response = http_post(uri, token, body)
|
|
84
|
+
data = parse_response(response)
|
|
85
|
+
|
|
86
|
+
cert_pem = data.dig("ephemeralCert", "cert")
|
|
87
|
+
raise ConnectionError, "Failed to retrieve ephemeral certificate" if cert_pem.nil?
|
|
88
|
+
|
|
89
|
+
cert = OpenSSL::X509::Certificate.new(cert_pem)
|
|
90
|
+
|
|
91
|
+
{
|
|
92
|
+
cert: cert_pem,
|
|
93
|
+
expiration: cert.not_after
|
|
94
|
+
}
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
private
|
|
98
|
+
|
|
99
|
+
def parse_response(response)
|
|
100
|
+
data = JSON.parse(response.body)
|
|
101
|
+
|
|
102
|
+
if response.code.to_i >= 400
|
|
103
|
+
error_msg = data.dig("error", "message") || response.body
|
|
104
|
+
raise ConnectionError, "API request failed: #{error_msg}"
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
data
|
|
108
|
+
rescue JSON::ParserError => e
|
|
109
|
+
raise ConnectionError, "Invalid API response: #{e.message}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Parse IP addresses from API response
|
|
113
|
+
# Node.js ref: https://github.com/GoogleCloudPlatform/cloud-sql-nodejs-connector/blob/main/src/sqladmin-fetcher.ts
|
|
114
|
+
def parse_ip_addresses(ip_data, dns_name, dns_names, psc_enabled)
|
|
115
|
+
ip_addresses = {}
|
|
116
|
+
|
|
117
|
+
# Parse regular IP addresses (PUBLIC/PRIVATE)
|
|
118
|
+
ip_data&.each do |ip|
|
|
119
|
+
case ip["type"]
|
|
120
|
+
when "PRIMARY"
|
|
121
|
+
ip_addresses["PRIMARY"] = ip["ipAddress"]
|
|
122
|
+
when "PRIVATE"
|
|
123
|
+
ip_addresses["PRIVATE"] = ip["ipAddress"]
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# PSC uses DNS names, not IP addresses
|
|
128
|
+
# First, check dns_names array for PSC connection type
|
|
129
|
+
if dns_names.is_a?(Array)
|
|
130
|
+
dns_names.each do |dnm|
|
|
131
|
+
if dnm["connectionType"] == "PRIVATE_SERVICE_CONNECT" && dnm["dnsScope"] == "INSTANCE"
|
|
132
|
+
ip_addresses["PSC"] = dnm["name"]
|
|
133
|
+
break
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Fallback to legacy dns_name field if PSC not found and pscEnabled is true
|
|
139
|
+
ip_addresses["PSC"] = dns_name if ip_addresses["PSC"].nil? && dns_name && psc_enabled
|
|
140
|
+
|
|
141
|
+
ip_addresses
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def http_get(uri, token)
|
|
145
|
+
http = create_http_client(uri)
|
|
146
|
+
|
|
147
|
+
request = Net::HTTP::Get.new(uri)
|
|
148
|
+
request["Authorization"] = "Bearer #{token}"
|
|
149
|
+
request["Content-Type"] = "application/json"
|
|
150
|
+
|
|
151
|
+
http.request(request)
|
|
152
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
153
|
+
raise ConnectionError, "Request timed out: #{e.message}"
|
|
154
|
+
rescue StandardError => e
|
|
155
|
+
raise ConnectionError, "HTTP request failed: #{e.message}"
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def http_post(uri, token, body)
|
|
159
|
+
http = create_http_client(uri)
|
|
160
|
+
|
|
161
|
+
request = Net::HTTP::Post.new(uri)
|
|
162
|
+
request["Authorization"] = "Bearer #{token}"
|
|
163
|
+
request["Content-Type"] = "application/json"
|
|
164
|
+
request.body = body.to_json
|
|
165
|
+
|
|
166
|
+
http.request(request)
|
|
167
|
+
rescue Net::OpenTimeout, Net::ReadTimeout => e
|
|
168
|
+
raise ConnectionError, "Request timed out: #{e.message}"
|
|
169
|
+
rescue StandardError => e
|
|
170
|
+
raise ConnectionError, "HTTP request failed: #{e.message}"
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def create_http_client(uri)
|
|
174
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
175
|
+
http.use_ssl = true
|
|
176
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
177
|
+
http.open_timeout = HTTP_TIMEOUT
|
|
178
|
+
http.read_timeout = HTTP_TIMEOUT
|
|
179
|
+
http
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 Martin Milo
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require "socket"
|
|
18
|
+
|
|
19
|
+
module CloudSQLRubyConnector
|
|
20
|
+
# Local TCP proxy that bridges pg (plain) to Cloud SQL (SSL)
|
|
21
|
+
#
|
|
22
|
+
# This proxy is necessary because Cloud SQL requires direct TLS connections,
|
|
23
|
+
# but libpq (PostgreSQL client) sends an SSLRequest message first, which
|
|
24
|
+
# Cloud SQL doesn't understand. The proxy accepts plain TCP connections
|
|
25
|
+
# locally and forwards them over the pre-established SSL connection.
|
|
26
|
+
class SslProxy
|
|
27
|
+
attr_reader :port
|
|
28
|
+
|
|
29
|
+
def initialize(ssl_socket)
|
|
30
|
+
@ssl_socket = ssl_socket
|
|
31
|
+
@server = TCPServer.new("127.0.0.1", 0) # Port 0 = kernel assigns available port
|
|
32
|
+
@port = @server.addr[1]
|
|
33
|
+
@running = false
|
|
34
|
+
@threads = []
|
|
35
|
+
@mutex = Mutex.new
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Start the proxy server in a background thread
|
|
39
|
+
def start
|
|
40
|
+
@running = true
|
|
41
|
+
@accept_thread = Thread.new { accept_loop }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Stop the proxy server and clean up resources
|
|
45
|
+
def stop
|
|
46
|
+
@mutex.synchronize do
|
|
47
|
+
@running = false
|
|
48
|
+
@accept_thread&.kill if @accept_thread&.alive?
|
|
49
|
+
@threads.each { |t| t.kill if t.alive? }
|
|
50
|
+
@threads.clear
|
|
51
|
+
begin
|
|
52
|
+
@server.close
|
|
53
|
+
rescue StandardError
|
|
54
|
+
nil
|
|
55
|
+
end
|
|
56
|
+
begin
|
|
57
|
+
@ssl_socket.close
|
|
58
|
+
rescue StandardError
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
private
|
|
65
|
+
|
|
66
|
+
def accept_loop
|
|
67
|
+
client = @server.accept
|
|
68
|
+
@server.close # Close listener immediately - we only need one client
|
|
69
|
+
|
|
70
|
+
@mutex.synchronize do
|
|
71
|
+
@threads << Thread.new { forward(client, @ssl_socket) }
|
|
72
|
+
@threads << Thread.new { forward(@ssl_socket, client) }
|
|
73
|
+
end
|
|
74
|
+
rescue IOError
|
|
75
|
+
# Server closed
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def forward(from, to)
|
|
79
|
+
while @running
|
|
80
|
+
data = from.readpartial(16_384)
|
|
81
|
+
to.write(data)
|
|
82
|
+
end
|
|
83
|
+
rescue EOFError, IOError, Errno::ECONNRESET, Errno::EPIPE
|
|
84
|
+
# Close the other side to unblock the paired thread
|
|
85
|
+
begin
|
|
86
|
+
to.close
|
|
87
|
+
rescue StandardError
|
|
88
|
+
nil
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 Martin Milo
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
module CloudSQLRubyConnector
|
|
18
|
+
VERSION = "1.0.0"
|
|
19
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Copyright 2026 Martin Milo
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
require_relative "cloud_sql_ruby_connector/version"
|
|
18
|
+
require_relative "cloud_sql_ruby_connector/errors"
|
|
19
|
+
require_relative "cloud_sql_ruby_connector/ip_address_types"
|
|
20
|
+
require_relative "cloud_sql_ruby_connector/auth_types"
|
|
21
|
+
require_relative "cloud_sql_ruby_connector/credentials/base"
|
|
22
|
+
require_relative "cloud_sql_ruby_connector/credentials/service_account"
|
|
23
|
+
require_relative "cloud_sql_ruby_connector/credentials/user_credentials"
|
|
24
|
+
require_relative "cloud_sql_ruby_connector/credentials/metadata"
|
|
25
|
+
require_relative "cloud_sql_ruby_connector/ssl_proxy"
|
|
26
|
+
require_relative "cloud_sql_ruby_connector/sqladmin_fetcher"
|
|
27
|
+
require_relative "cloud_sql_ruby_connector/postgresql/connector"
|
|
28
|
+
|
|
29
|
+
# Cloud SQL Ruby Connector
|
|
30
|
+
#
|
|
31
|
+
# A Ruby connector for Google Cloud SQL that provides secure, IAM-based
|
|
32
|
+
# authentication without requiring the Cloud SQL Auth Proxy.
|
|
33
|
+
#
|
|
34
|
+
# @example Basic usage with PostgreSQL
|
|
35
|
+
# require 'cloud_sql_ruby_connector'
|
|
36
|
+
# require 'pg'
|
|
37
|
+
#
|
|
38
|
+
# connector = CloudSQLRubyConnector::PostgreSQL::Connector.new("my-project:us-central1:my-instance")
|
|
39
|
+
# conn = connector.connect(user: "myuser", password: "mypass", dbname: "mydb")
|
|
40
|
+
# result = conn.exec("SELECT NOW()")
|
|
41
|
+
# puts result.first
|
|
42
|
+
# conn.close
|
|
43
|
+
# connector.close
|
|
44
|
+
#
|
|
45
|
+
# @example Using IAM authentication
|
|
46
|
+
# connector = CloudSQLRubyConnector::PostgreSQL::Connector.new(
|
|
47
|
+
# "my-project:us-central1:my-instance",
|
|
48
|
+
# auth_type: :iam,
|
|
49
|
+
# ip_type: :private
|
|
50
|
+
# )
|
|
51
|
+
# conn = connector.connect(
|
|
52
|
+
# user: "service-account@project.iam.gserviceaccount.com",
|
|
53
|
+
# dbname: "mydb"
|
|
54
|
+
# )
|
|
55
|
+
#
|
|
56
|
+
module CloudSQLRubyConnector
|
|
57
|
+
module PostgreSQL
|
|
58
|
+
end
|
|
59
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: cloud_sql_ruby_connector
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Martin Milo
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 2026-01-17 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.2'
|
|
26
|
+
description: An unofficial Ruby connector for Google Cloud SQL that provides secure,
|
|
27
|
+
IAM-based authentication without requiring the Cloud SQL Auth Proxy.
|
|
28
|
+
email:
|
|
29
|
+
- milomartin.za@gmail.com
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- CHANGELOG.md
|
|
35
|
+
- CODE_OF_CONDUCT.md
|
|
36
|
+
- CONTRIBUTING.md
|
|
37
|
+
- LICENSE
|
|
38
|
+
- README.md
|
|
39
|
+
- SECURITY.md
|
|
40
|
+
- lib/cloud_sql_ruby_connector.rb
|
|
41
|
+
- lib/cloud_sql_ruby_connector/auth_types.rb
|
|
42
|
+
- lib/cloud_sql_ruby_connector/credentials/base.rb
|
|
43
|
+
- lib/cloud_sql_ruby_connector/credentials/metadata.rb
|
|
44
|
+
- lib/cloud_sql_ruby_connector/credentials/service_account.rb
|
|
45
|
+
- lib/cloud_sql_ruby_connector/credentials/user_credentials.rb
|
|
46
|
+
- lib/cloud_sql_ruby_connector/errors.rb
|
|
47
|
+
- lib/cloud_sql_ruby_connector/ip_address_types.rb
|
|
48
|
+
- lib/cloud_sql_ruby_connector/postgresql/connector.rb
|
|
49
|
+
- lib/cloud_sql_ruby_connector/rails.rb
|
|
50
|
+
- lib/cloud_sql_ruby_connector/sqladmin_fetcher.rb
|
|
51
|
+
- lib/cloud_sql_ruby_connector/ssl_proxy.rb
|
|
52
|
+
- lib/cloud_sql_ruby_connector/version.rb
|
|
53
|
+
homepage: https://github.com/martinmilo/cloud-sql-ruby-connector
|
|
54
|
+
licenses:
|
|
55
|
+
- Apache-2.0
|
|
56
|
+
metadata:
|
|
57
|
+
homepage_uri: https://github.com/martinmilo/cloud-sql-ruby-connector
|
|
58
|
+
source_code_uri: https://github.com/martinmilo/cloud-sql-ruby-connector
|
|
59
|
+
changelog_uri: https://github.com/martinmilo/cloud-sql-ruby-connector/blob/main/CHANGELOG.md
|
|
60
|
+
rubygems_mfa_required: 'true'
|
|
61
|
+
rdoc_options: []
|
|
62
|
+
require_paths:
|
|
63
|
+
- lib
|
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 3.3.0
|
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
|
+
requirements:
|
|
71
|
+
- - ">="
|
|
72
|
+
- !ruby/object:Gem::Version
|
|
73
|
+
version: '0'
|
|
74
|
+
requirements: []
|
|
75
|
+
rubygems_version: 3.6.2
|
|
76
|
+
specification_version: 4
|
|
77
|
+
summary: Cloud SQL Ruby Connector
|
|
78
|
+
test_files: []
|