mirror of
https://github.com/fog/fog.git
synced 2022-11-09 13:51:43 -05:00
Merge pull request #2977 from frodenas/gce_sql
[google|sql] Initial support for Google Cloud SQL
This commit is contained in:
commit
70a549dbb8
9 changed files with 402 additions and 0 deletions
|
@ -6,6 +6,8 @@ module Google # deviates from other bin stuff to accomodate gem
|
||||||
Fog::Compute::Google
|
Fog::Compute::Google
|
||||||
when :storage
|
when :storage
|
||||||
Fog::Storage::Google
|
Fog::Storage::Google
|
||||||
|
when :sql
|
||||||
|
Fog::Google::SQL
|
||||||
else
|
else
|
||||||
raise ArgumentError, "Unsupported #{self} service: #{key}"
|
raise ArgumentError, "Unsupported #{self} service: #{key}"
|
||||||
end
|
end
|
||||||
|
@ -20,6 +22,8 @@ module Google # deviates from other bin stuff to accomodate gem
|
||||||
when :compute
|
when :compute
|
||||||
Fog::Logger.warning("Google[:compute] is not recommended, use Compute[:google] for portability")
|
Fog::Logger.warning("Google[:compute] is not recommended, use Compute[:google] for portability")
|
||||||
Fog::Compute.new(:provider => 'Google')
|
Fog::Compute.new(:provider => 'Google')
|
||||||
|
when :sql
|
||||||
|
Fog::Google::SQL.new
|
||||||
else
|
else
|
||||||
raise ArgumentError, "Unrecognized service: #{key.inspect}"
|
raise ArgumentError, "Unrecognized service: #{key.inspect}"
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
require 'fog/google/compute'
|
require 'fog/google/compute'
|
||||||
require 'fog/google/storage'
|
require 'fog/google/storage'
|
||||||
|
require 'fog/google/sql'
|
||||||
|
|
|
@ -7,6 +7,7 @@ module Fog
|
||||||
|
|
||||||
service(:compute, 'Compute')
|
service(:compute, 'Compute')
|
||||||
service(:storage, 'Storage')
|
service(:storage, 'Storage')
|
||||||
|
service(:sql, 'SQL')
|
||||||
|
|
||||||
class Mock
|
class Mock
|
||||||
def self.etag
|
def self.etag
|
||||||
|
@ -18,5 +19,160 @@ module Fog
|
||||||
rand(max).to_s(16)
|
rand(max).to_s(16)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module Shared
|
||||||
|
attr_reader :project, :api_version, :api_url
|
||||||
|
|
||||||
|
##
|
||||||
|
# Initializes shared attributes
|
||||||
|
#
|
||||||
|
# @param [String] project Google Cloud Project
|
||||||
|
# @param [String] api_version Google API version
|
||||||
|
# @param [String] base_url Google API base url
|
||||||
|
# @return [void]
|
||||||
|
def shared_initialize(project, api_version, base_url)
|
||||||
|
@project = project
|
||||||
|
@api_version = api_version
|
||||||
|
@api_url = base_url + api_version + '/projects/'
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Initializes the Google API Client
|
||||||
|
#
|
||||||
|
# @param [Hash] options Google API options
|
||||||
|
# @option options [String] :google_client_email A @developer.gserviceaccount.com email address to use
|
||||||
|
# @option options [String] :google_key_location The location of a pkcs12 key file
|
||||||
|
# @option options [String] :google_key_string The content of the pkcs12 key file
|
||||||
|
# @option options [String] :google_api_scope_url The access scope URLs
|
||||||
|
# @option options [String] :app_name The app name to set in the user agent
|
||||||
|
# @option options [String] :app_version The app version to set in the user agent
|
||||||
|
# @option options [Google::APIClient] :google_client Existing Google API Client
|
||||||
|
# @return [Google::APIClient] Google API Client
|
||||||
|
# @raises [ArgumentError] If there is any missing argument
|
||||||
|
def initialize_google_client(options)
|
||||||
|
# NOTE: loaded here to avoid requiring this as a core Fog dependency
|
||||||
|
begin
|
||||||
|
require 'google/api_client'
|
||||||
|
rescue LoadError => error
|
||||||
|
Fog::Logger.warning('Please install the google-api-client gem before using this provider')
|
||||||
|
raise error
|
||||||
|
end
|
||||||
|
|
||||||
|
# User can provide an existing Google API Client
|
||||||
|
client = options[:google_client]
|
||||||
|
return client unless client.nil?
|
||||||
|
|
||||||
|
# Validate required arguments
|
||||||
|
unless options[:google_client_email]
|
||||||
|
raise ArgumentError.new('Missing required arguments: google_client_email')
|
||||||
|
end
|
||||||
|
|
||||||
|
if options[:google_key_location]
|
||||||
|
google_key = File.expand_path(options[:google_key_location])
|
||||||
|
elsif options[:google_key_string]
|
||||||
|
google_key = options[:google_key_string]
|
||||||
|
else
|
||||||
|
raise ArgumentError.new('Missing required arguments: google_key_location or google_key_string')
|
||||||
|
end
|
||||||
|
|
||||||
|
unless options[:google_api_scope_url]
|
||||||
|
raise ArgumentError.new('Missing required arguments: google_api_scope_url')
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a new Google API Client
|
||||||
|
self.new_pk12_google_client(
|
||||||
|
options[:google_client_email],
|
||||||
|
google_key,
|
||||||
|
options[:google_api_scope_url],
|
||||||
|
options[:app_name],
|
||||||
|
options[:app_version]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Create a Google API Client with a user email and a pkcs12 key
|
||||||
|
#
|
||||||
|
# @param [String] google_client_email A @developer.gserviceaccount.com email address to use
|
||||||
|
# @param [String] google_key An absolute location to a pkcs12 key file or the content of the file itself
|
||||||
|
# @param [String] google_api_scope_url Access scope URLs
|
||||||
|
# @param [String] app_name The app name to set in the user agent
|
||||||
|
# @param [String] app_version The app version to set in the user agent
|
||||||
|
# @return [Google::APIClient] Google API Client
|
||||||
|
def new_pk12_google_client(google_client_email, google_key, google_api_scope_url, app_name = nil, app_version = nil)
|
||||||
|
application_name = app_name.nil? ? 'fog' : "#{app_name}/#{app_version || '0.0.0'} fog"
|
||||||
|
api_client_options = {
|
||||||
|
:application_name => application_name,
|
||||||
|
:application_version => Fog::VERSION,
|
||||||
|
}
|
||||||
|
client = ::Google::APIClient.new(api_client_options)
|
||||||
|
|
||||||
|
client.authorization = Signet::OAuth2::Client.new(
|
||||||
|
{
|
||||||
|
:audience => 'https://accounts.google.com/o/oauth2/token',
|
||||||
|
:auth_provider_x509_cert_url => 'https://www.googleapis.com/oauth2/v1/certs',
|
||||||
|
:client_x509_cert_url => "https://www.googleapis.com/robot/v1/metadata/x509/#{google_client_email}",
|
||||||
|
:issuer => google_client_email,
|
||||||
|
:scope => google_api_scope_url,
|
||||||
|
:signing_key => ::Google::APIClient::KeyUtils.load_from_pkcs12(google_key, 'notasecret'),
|
||||||
|
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
|
||||||
|
}
|
||||||
|
)
|
||||||
|
client.authorization.fetch_access_token!
|
||||||
|
|
||||||
|
client
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Executes a request and wraps it in a result object
|
||||||
|
#
|
||||||
|
# @param [Google::APIClient::Method] api_method The method object or the RPC name of the method being executed
|
||||||
|
# @param [Hash] parameters The parameters to send to the method
|
||||||
|
# @param [Hash] body_object The body object of the request
|
||||||
|
# @return [Excon::Response] The result from the API
|
||||||
|
def request(api_method, parameters, body_object = nil)
|
||||||
|
client_parms = {
|
||||||
|
:api_method => api_method,
|
||||||
|
:parameters => parameters,
|
||||||
|
}
|
||||||
|
client_parms[:body_object] = body_object if body_object
|
||||||
|
|
||||||
|
result = @client.execute(client_parms)
|
||||||
|
|
||||||
|
build_excon_response(result.body.nil? || result.body.empty? ? nil : Fog::JSON.decode(result.body), result.status)
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Builds an Excon response
|
||||||
|
#
|
||||||
|
# @param [Hash] Response body
|
||||||
|
# @param [Integer] Response status
|
||||||
|
# @return [Excon::Response] Excon response
|
||||||
|
def build_excon_response(body, status = 200)
|
||||||
|
response = Excon::Response.new(:body => body, :status => status)
|
||||||
|
if body && body.has_key?('error')
|
||||||
|
msg = 'Google Cloud did not return an error message'
|
||||||
|
|
||||||
|
if body['error'].kind_of?(Hash)
|
||||||
|
response.status = body['error']['code']
|
||||||
|
if body['error'].has_key?('errors')
|
||||||
|
msg = body['error']['errors'].map{ |error| error['message'] }.join(', ')
|
||||||
|
elsif body['error'].has_key?('message')
|
||||||
|
msg = body['error']['message']
|
||||||
|
end
|
||||||
|
elsif body['error'].kind_of?(Array)
|
||||||
|
msg = body['error'].map{ |error| error['code'] }.join(', ')
|
||||||
|
end
|
||||||
|
|
||||||
|
case response.status
|
||||||
|
when 404
|
||||||
|
raise Fog::Errors::NotFound.new(msg)
|
||||||
|
else
|
||||||
|
raise Fog::Errors::Error.new(msg)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
response
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
20
lib/fog/google/models/sql/tier.rb
Normal file
20
lib/fog/google/models/sql/tier.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
require 'fog/core/model'
|
||||||
|
|
||||||
|
module Fog
|
||||||
|
module Google
|
||||||
|
class SQL
|
||||||
|
##
|
||||||
|
# A Google Cloud SQL service tier resource
|
||||||
|
#
|
||||||
|
# @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/tiers
|
||||||
|
class Tier < Fog::Model
|
||||||
|
identity :tier
|
||||||
|
|
||||||
|
attribute :disk_quota, :aliases => 'DiskQuota'
|
||||||
|
attribute :kind
|
||||||
|
attribute :ram, :aliases => 'RAM'
|
||||||
|
attribute :region
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
lib/fog/google/models/sql/tiers.rb
Normal file
21
lib/fog/google/models/sql/tiers.rb
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
require 'fog/core/collection'
|
||||||
|
require 'fog/google/models/sql/tier'
|
||||||
|
|
||||||
|
module Fog
|
||||||
|
module Google
|
||||||
|
class SQL
|
||||||
|
class Tiers < Fog::Collection
|
||||||
|
model Fog::Google::SQL::Tier
|
||||||
|
|
||||||
|
##
|
||||||
|
# Lists all available service tiers
|
||||||
|
#
|
||||||
|
# @return [Array<Fog::Google::SQL::Tier>] List of tiers
|
||||||
|
def all
|
||||||
|
data = service.list_tiers.body['items'] || []
|
||||||
|
load(data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
82
lib/fog/google/requests/sql/list_tiers.rb
Normal file
82
lib/fog/google/requests/sql/list_tiers.rb
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
module Fog
|
||||||
|
module Google
|
||||||
|
class SQL
|
||||||
|
##
|
||||||
|
# Lists all available service tiers for Google Cloud SQL
|
||||||
|
#
|
||||||
|
# @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/tiers/list
|
||||||
|
|
||||||
|
class Real
|
||||||
|
def list_tiers
|
||||||
|
api_method = @sql.tiers.list
|
||||||
|
parameters = {
|
||||||
|
'project' => @project,
|
||||||
|
}
|
||||||
|
|
||||||
|
request(api_method, parameters)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Mock
|
||||||
|
def list_tiers
|
||||||
|
body = {
|
||||||
|
'kind' => 'sql#tiersList',
|
||||||
|
'items' => [
|
||||||
|
{
|
||||||
|
'kind' => 'sql#tier',
|
||||||
|
'tier' => 'D0',
|
||||||
|
'RAM' => '134217728',
|
||||||
|
'DiskQuota' => '268435456000',
|
||||||
|
'region' => ['us-central', 'europe-west1', 'asia-east1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kind' => 'sql#tier',
|
||||||
|
'tier' => 'D1',
|
||||||
|
'RAM' => '536870912',
|
||||||
|
'DiskQuota' => '268435456000',
|
||||||
|
'region' => ['us-central', 'europe-west1', 'asia-east1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kind' => 'sql#tier',
|
||||||
|
'tier' => 'D2',
|
||||||
|
'RAM' => '1073741824',
|
||||||
|
'DiskQuota' => '268435456000',
|
||||||
|
'region' => ['us-central', 'europe-west1', 'asia-east1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kind' => 'sql#tier',
|
||||||
|
'tier' => 'D4',
|
||||||
|
'RAM' => '2147483648',
|
||||||
|
'DiskQuota' => '268435456000',
|
||||||
|
'region' => ['us-central', 'europe-west1', 'asia-east1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kind' => 'sql#tier',
|
||||||
|
'tier' => 'D8',
|
||||||
|
'RAM' => '4294967296',
|
||||||
|
'DiskQuota' => '268435456000',
|
||||||
|
'region' => ['us-central', 'europe-west1', 'asia-east1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kind' => 'sql#tier',
|
||||||
|
'tier' => 'D16',
|
||||||
|
'RAM' => '8589934592',
|
||||||
|
'DiskQuota' => '268435456000',
|
||||||
|
'region' => ['us-central', 'europe-west1', 'asia-east1'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'kind' => 'sql#tier',
|
||||||
|
'tier' => 'D32',
|
||||||
|
'RAM' => '17179869184',
|
||||||
|
'DiskQuota' => '268435456000',
|
||||||
|
'region' => ['us-central'],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
build_excon_response(body)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
81
lib/fog/google/sql.rb
Normal file
81
lib/fog/google/sql.rb
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
require 'fog/google/core'
|
||||||
|
|
||||||
|
module Fog
|
||||||
|
module Google
|
||||||
|
class SQL < Fog::Service
|
||||||
|
requires :google_project
|
||||||
|
recognizes :google_client_email, :google_key_location, :google_key_string, :google_client,
|
||||||
|
:app_name, :app_version
|
||||||
|
|
||||||
|
GOOGLE_SQL_API_VERSION = 'v1beta3'
|
||||||
|
GOOGLE_SQL_BASE_URL = 'https://www.googleapis.com/sql/'
|
||||||
|
GOOGLE_SQL_API_SCOPE_URLS = %w(https://www.googleapis.com/auth/sqlservice.admin
|
||||||
|
https://www.googleapis.com/auth/cloud-platform)
|
||||||
|
|
||||||
|
##
|
||||||
|
# MODELS
|
||||||
|
model_path 'fog/google/models/sql'
|
||||||
|
|
||||||
|
# Tier
|
||||||
|
model :tier
|
||||||
|
collection :tiers
|
||||||
|
|
||||||
|
##
|
||||||
|
# REQUESTS
|
||||||
|
request_path 'fog/google/requests/sql'
|
||||||
|
|
||||||
|
# Tier
|
||||||
|
request :list_tiers
|
||||||
|
|
||||||
|
class Mock
|
||||||
|
include Fog::Google::Shared
|
||||||
|
|
||||||
|
def initialize(options)
|
||||||
|
shared_initialize(options[:google_project], GOOGLE_SQL_API_VERSION, GOOGLE_SQL_BASE_URL)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.data
|
||||||
|
@data ||= Hash.new do |hash, key|
|
||||||
|
hash[key] = {
|
||||||
|
:backup_runs => {},
|
||||||
|
:instances => {},
|
||||||
|
:operations => {},
|
||||||
|
:ssl_certs => {},
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.reset
|
||||||
|
@data = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def data
|
||||||
|
self.class.data[project]
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_data
|
||||||
|
self.class.data.delete(project)
|
||||||
|
end
|
||||||
|
|
||||||
|
def random_operation
|
||||||
|
"operation-#{Fog::Mock.random_numbers(13)}-#{Fog::Mock.random_hex(13)}-#{Fog::Mock.random_hex(8)}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Real
|
||||||
|
include Fog::Google::Shared
|
||||||
|
|
||||||
|
attr_accessor :client
|
||||||
|
attr_reader :sql
|
||||||
|
|
||||||
|
def initialize(options)
|
||||||
|
shared_initialize(options[:google_project], GOOGLE_SQL_API_VERSION, GOOGLE_SQL_BASE_URL)
|
||||||
|
options.merge!(:google_api_scope_url => GOOGLE_SQL_API_SCOPE_URLS.join(' '))
|
||||||
|
|
||||||
|
@client = initialize_google_client(options)
|
||||||
|
@sql = @client.discovered_api('sqladmin', api_version)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
tests/google/models/sql/tiers_tests.rb
Normal file
12
tests/google/models/sql/tiers_tests.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Shindo.tests('Fog::Google[:sql] | tiers model', ['google']) do
|
||||||
|
@tiers = Fog::Google[:sql].tiers
|
||||||
|
|
||||||
|
tests('success') do
|
||||||
|
|
||||||
|
tests('#all').succeeds do
|
||||||
|
@tiers.all
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
25
tests/google/requests/sql/tier_tests.rb
Normal file
25
tests/google/requests/sql/tier_tests.rb
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
Shindo.tests('Fog::Google[:sql] | tier requests', ['google']) do
|
||||||
|
@sql = Fog::Google[:sql]
|
||||||
|
|
||||||
|
@get_tier_format = {
|
||||||
|
'tier' => String,
|
||||||
|
'DiskQuota' => String,
|
||||||
|
'kind' => String,
|
||||||
|
'RAM' => String,
|
||||||
|
'region' => Array,
|
||||||
|
}
|
||||||
|
|
||||||
|
@list_tiers_format = {
|
||||||
|
'kind' => String,
|
||||||
|
'items' => [@get_tier_format],
|
||||||
|
}
|
||||||
|
|
||||||
|
tests('success') do
|
||||||
|
|
||||||
|
tests('#list_tiers').formats(@list_tiers_format) do
|
||||||
|
@sql.list_tiers.body
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
Loading…
Reference in a new issue