1
0
Fork 0
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:
Nat Welch 2014-07-20 18:08:58 +01:00
commit 70a549dbb8
9 changed files with 402 additions and 0 deletions

View file

@ -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

View file

@ -1,2 +1,3 @@
require 'fog/google/compute' require 'fog/google/compute'
require 'fog/google/storage' require 'fog/google/storage'
require 'fog/google/sql'

View file

@ -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

View 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

View 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

View 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
View 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

View 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

View 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