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
|
||||
when :storage
|
||||
Fog::Storage::Google
|
||||
when :sql
|
||||
Fog::Google::SQL
|
||||
else
|
||||
raise ArgumentError, "Unsupported #{self} service: #{key}"
|
||||
end
|
||||
|
@ -20,6 +22,8 @@ module Google # deviates from other bin stuff to accomodate gem
|
|||
when :compute
|
||||
Fog::Logger.warning("Google[:compute] is not recommended, use Compute[:google] for portability")
|
||||
Fog::Compute.new(:provider => 'Google')
|
||||
when :sql
|
||||
Fog::Google::SQL.new
|
||||
else
|
||||
raise ArgumentError, "Unrecognized service: #{key.inspect}"
|
||||
end
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
require 'fog/google/compute'
|
||||
require 'fog/google/storage'
|
||||
require 'fog/google/sql'
|
||||
|
|
|
@ -7,6 +7,7 @@ module Fog
|
|||
|
||||
service(:compute, 'Compute')
|
||||
service(:storage, 'Storage')
|
||||
service(:sql, 'SQL')
|
||||
|
||||
class Mock
|
||||
def self.etag
|
||||
|
@ -18,5 +19,160 @@ module Fog
|
|||
rand(max).to_s(16)
|
||||
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
|
||||
|
|
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