diff --git a/lib/fog/google/models/sql/instance.rb b/lib/fog/google/models/sql/instance.rb new file mode 100644 index 000000000..90fe0be76 --- /dev/null +++ b/lib/fog/google/models/sql/instance.rb @@ -0,0 +1,353 @@ +require 'fog/core/model' + +module Fog + module Google + class SQL + class Instance < Fog::Model + identity :instance + + attribute :current_disk_size, :aliases => 'currentDiskSize' + attribute :database_version, :aliases => 'databaseVersion' + attribute :etag + attribute :ip_addresses, :aliases => 'ipAddresses' + attribute :kind + attribute :max_disk_size, :aliases => 'maxDiskSize' + attribute :project + attribute :region + attribute :server_ca_cert, :aliases => 'serverCaCert' + attribute :settings + attribute :state + + # These attributes are not available in the representation of an 'Instance' returned by the Google SQL API. + attribute :activation_policy + attribute :autorized_gae_applications + attribute :backup_configuration + attribute :database_flags + attribute :ip_configuration_authorized_networks + attribute :ip_configuration_enabled + attribute :ip_configuration_require_ssl + attribute :location_preference_zone_follow_gae_application + attribute :location_preference_zone + attribute :pricing_plan + attribute :replication_type + attribute :settings_version + attribute :tier + + MAINTENANCE_STATE = 'MAINTENANCE' + PENDING_CREATE_STATE = 'PENDING_CREATE' + RUNNABLE_STATE = 'RUNNABLE' + SUSPENDED_STATE = 'SUSPENDED' + UNKNOWN_STATE = 'UNKNOWN_STATE' + + ## + # Returns the activation policy for this instance + # + # @return [String] The activation policy for this instance + def activation_policy + self.settings['activationPolicy'] + end + + ## + # Returns the AppEngine app ids that can access this instance + # + # @return [Array] The AppEngine app ids that can access this instance + def autorized_gae_applications + self.settings['authorizedGaeApplications'] + end + + ## + # Returns the daily backup configuration for the instance + # + # @return [Array] The daily backup configuration for the instance + def backup_configuration + self.settings['backupConfiguration'] + end + + ## + # Creates a Cloud SQL instance as a clone of the source instance + # + # @param [String] destination_name Name of the Cloud SQL instance to be created as a clone + # @param [Hash] options Method options + # @option options [String] :log_filename Name of the binary log file for a Cloud SQL instance + # @option options [Integer] :log_position Position (offset) within the binary log file + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def clone(destination_name, options = {}) + requires :identity + + data = service.clone_instance(self.identity, destination_name, options) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + operation.wait_for { ready? } + end + operation + end + + ## + # Creates a Cloud SQL instance + # + # @return [Fog::Google::SQL::Instance] Instance resource + def create + requires :identity + + data = service.insert_instance(self.identity, self.attributes[:tier], self.attributes) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + operation.wait_for { !pending? } + reload + end + + ## + # Returns the database flags passed to the instance at startup + # + # @return [Array] The database flags passed to the instance at startup + def database_flags + self.settings['databaseFlags'] + end + + ## + # Deletes a Cloud SQL instance + # + # @param [Hash] options Method options + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def destroy(options = {}) + requires :identity + + data = service.delete_instance(self.identity) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + # DISABLED: A delete instance operation never reachs a 'DONE' state (bug?) + # operation.wait_for { ready? } + end + operation + end + + ## + # Exports data from a Cloud SQL instance to a Google Cloud Storage bucket as a MySQL dump file + # + # @param [String] uri The path to the file in Google Cloud Storage where the export will be stored, + # or where it was already stored + # @param [Hash] options Method options + # @option options [Array] :databases Databases (for example, guestbook) from which the export is made. + # If unspecified, all databases are exported. + # @option options [Array] :tables Tables to export, or that were exported, from the specified database. + # If you specify tables, specify one and only one database. + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def export(uri, options = {}) + requires :identity + + data = service.export_instance(self.identity, uri, options) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + operation.wait_for { ready? } + end + operation + end + + ## + # Imports data into a Cloud SQL instance from a MySQL dump file in Google Cloud Storage + # + # @param [Array] uri A path to the MySQL dump file in Google Cloud Storage from which the import is + # made + # @param [Hash] options Method options + # @option options [String] :database The database (for example, guestbook) to which the import is made. + # If not set, it is assumed that the database is specified in the file to be imported. + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def import(uri, options = {}) + requires :identity + + data = service.import_instance(self.identity, uri, options) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + operation.wait_for { ready? } + end + operation + end + + ## + # Returns the list of external networks that are allowed to connect to the instance using the IP + # + # @return [Array] The list of external networks that are allowed to connect to the instance using the IP + def ip_configuration_authorized_networks + self.settings.fetch('ipConfiguration', {})['authorizedNetworks'] + end + + ## + # Returns whether the instance should be assigned an IP address or not + # + # @return [Boolean] Whether the instance should be assigned an IP address or not + def ip_configuration_enabled + self.settings.fetch('ipConfiguration', {})['enabled'] + end + + ## + # Returns whether the mysqld should default to 'REQUIRE X509' for users connecting over IP + # + # @return [Boolean] Whether the mysqld should default to 'REQUIRE X509' for users connecting over IP + def ip_configuration_require_ssl + self.settings.fetch('ipConfiguration', {})['requireSsl'] + end + + ## + # Returns the AppEngine application to follow + # + # @return [String] The AppEngine application to follow + def location_preference_zone_follow_gae_application + self.settings.fetch('locationPreference', {})['followGaeApplication'] + end + + ## + # Returns the preferred Compute Engine zone + # + # @return [String] The preferred Compute Engine zone + def location_preference_zone + self.settings.fetch('locationPreference', {})['zone'] + end + + ## + # Returns the pricing plan for this instance + # + # @return [String] The pricing plan for this instance + def pricing_plan + self.settings['pricingPlan'] + end + + ## + # Checks if the instance is running + # + # @return [Boolean] True if the instance is running; False otherwise + def ready? + self.state == RUNNABLE_STATE + end + + ## + # Returns the type of replication this instance uses + # + # @return [String] The type of replication this instance uses + def replication_type + self.settings['replicationType'] + end + + ## + # Deletes all client certificates and generates a new server SSL certificate for the instance + # + # @param [Hash] options Method options + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def reset_ssl_config(options = {}) + requires :identity + + data = service.reset_instance_ssl_config(self.identity) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + operation.wait_for { ready? } + end + operation + end + + ## + # Restarts a Cloud SQL instance + # + # @param [Hash] options Method options + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def restart(options = {}) + requires :identity + + data = service.restart_instance(self.identity) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + operation.wait_for { ready? } + end + operation + end + + ## + # Restores a backup of a Cloud SQL instance + # + # @param [String] backup_configuration The identifier of the backup configuration + # @param [String] due_time The time when this run is due to start in RFC 3339 format + # @param [Hash] options Method options + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def restore_backup(backup_configuration, due_time, options = {}) + requires :identity + + data = service.restore_instance_backup(self.identity, backup_configuration, due_time) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + operation.wait_for { ready? } + end + operation + end + + ## + # Saves a Cloud SQL instance + # + # @return [Fog::Google::SQL::Instance] Instance resource + def save + self.etag ? update : create + end + + ## + # Sets the password for the root user + # + # @param [String] password The password for the root user + # @param [Hash] options Method options + # @option options [Boolean] :async If the operation must be performed asynchronously (true by default) + # @return [Fog::Google::SQL::Operation] A Operation resource + def set_root_password(password, options = {}) + requires :identity + + data = service.set_instance_root_password(self.identity, password) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + unless options.fetch(:async, true) + operation.wait_for { ready? } + end + operation + end + + ## + # Returns the version of instance settings + # + # @return [String] The version of instance settings + def settings_version + self.settings['settingsVersion'] + end + + ## + # Lists all of the current SSL certificates for the instance + # + # @return [Array] List of SSL certificate resources + def ssl_certs + requires :identity + + service.ssl_certs.all(self.identity) + end + + ## + # Returns the tier of service for this instance + # + # @return [String] The tier of service for this instance + def tier + self.settings['tier'] + end + + ## + # Updates settings of a Cloud SQL instance + # + # @return [Fog::Google::SQL::Instance] Instance resource + def update + requires :identity + + data = service.update_instance(self.identity, self.settings_version, self.tier, self.attributes) + operation = Fog::Google::SQL::Operations.new(:service => service).get(self.instance, data.body['operation']) + operation.wait_for { !pending? } + reload + end + end + end + end +end diff --git a/lib/fog/google/models/sql/instances.rb b/lib/fog/google/models/sql/instances.rb new file mode 100644 index 000000000..b82228039 --- /dev/null +++ b/lib/fog/google/models/sql/instances.rb @@ -0,0 +1,39 @@ +require 'fog/core/collection' +require 'fog/google/models/sql/instance' + +module Fog + module Google + class SQL + class Instances < Fog::Collection + model Fog::Google::SQL::Instance + + ## + # Lists all instance + # + # @return [Array] List of instance resources + def all + data = service.list_instances.body['items'] || [] + load(data) + end + + ## + # Retrieves an instance + # + # @param [String] instance_id Instance ID + # @return [Fog::Google::SQL::Instance] Instance resource + def get(instance_id) + if instance = service.get_instance(instance_id).body + new(instance) + end + rescue Fog::Errors::NotFound + nil + rescue Fog::Errors::Error => e + # Google SQL returns a 403 if we try to access a non-existing resource + # The default behaviour in Fog is to return a nil + return nil if e.message == 'The client is not authorized to make this request.' + raise e + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/clone_instance.rb b/lib/fog/google/requests/sql/clone_instance.rb new file mode 100644 index 000000000..308ade43c --- /dev/null +++ b/lib/fog/google/requests/sql/clone_instance.rb @@ -0,0 +1,84 @@ +module Fog + module Google + class SQL + ## + # Creates a Cloud SQL instance as a clone of the source instance + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/clone + + class Real + def clone_instance(instance_id, destination_name, options = {}) + # The @sql.instances.clone method is overrided by the standard Ruby clone method + # so we cannot call it because it will just clone the @sql.instances instance. + # Instead we need to find the proper method trough the discovered_methods. + api_method = @sql.instances.discovered_methods.find { |x| x.id == 'sql.instances.clone' } + parameters = { + 'project' => @project, + } + + body = { + 'cloneContext' => { + 'kind' => 'sql#cloneContext', + 'sourceInstanceName' => instance_id, + 'destinationInstanceName' => destination_name, + } + } + + if options[:log_position] + body['cloneContext']['binLogCoordinates'] = { + 'kind' => 'sql#binLogCoordinates', + 'binLogFileName' => options[:log_filename], + 'binLogPosition' => options[:log_position], + } + end + + request(api_method, parameters, body) + end + end + + class Mock + def clone_instance(instance_id, destination_name, options = {}) + self.data[:instances][destination_name] = self.data[:instances][instance_id] + self.data[:instances][destination_name]['instance'] = destination_name + self.data[:ssl_certs][destination_name] = {} + self.data[:backup_runs][destination_name] = {} + + operation = self.random_operation + self.data[:operations][destination_name] ||= {} + self.data[:operations][destination_name][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => destination_name, + 'operation' => operation, + 'operationType' => 'CREATE', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'CLONE', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesClone', + 'operation' => operation, + } + + build_excon_response(body) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/delete_instance.rb b/lib/fog/google/requests/sql/delete_instance.rb new file mode 100644 index 000000000..d662fb812 --- /dev/null +++ b/lib/fog/google/requests/sql/delete_instance.rb @@ -0,0 +1,67 @@ +module Fog + module Google + class SQL + ## + # Deletes a Cloud SQL instance + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/delete + + class Real + def delete_instance(instance_id) + api_method = @sql.instances.delete + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + request(api_method, parameters) + end + end + + class Mock + def delete_instance(instance_id) + if self.data[:instances].has_key?(instance_id) + self.data[:instances].delete(instance_id) + self.data[:ssl_certs].delete(instance_id) + self.data[:backup_runs].delete(instance_id) + + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'DELETE', + 'state' => Fog::Google::SQL::Operation::PENDING_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesDelete', + 'operation' => operation, + } + status = 200 + else + body = { + 'error' => { + 'errors' => [ + { + 'domain' => 'global', + 'reason' => 'notAuthorized', + 'message' => 'The client is not authorized to make this request.', + } + ], + 'code' => 403, + 'message' => 'The client is not authorized to make this request.', + } + } + status = 403 + end + + build_excon_response(body, status) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/export_instance.rb b/lib/fog/google/requests/sql/export_instance.rb new file mode 100644 index 000000000..8bce35d63 --- /dev/null +++ b/lib/fog/google/requests/sql/export_instance.rb @@ -0,0 +1,56 @@ +module Fog + module Google + class SQL + ## + # Exports data from a Cloud SQL instance to a Google Cloud Storage bucket as a MySQL dump file + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/export + + class Real + def export_instance(instance_id, uri, options = {}) + api_method = @sql.instances.export + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + body = { + 'exportContext' => { + 'kind' => 'sql#exportContext', + 'uri' => uri, + 'database' => Array(options[:databases]), + 'table' => Array(options[:tables]), + } + } + + request(api_method, parameters, body) + end + end + + class Mock + def export_instance(instance_id, uri, options = {}) + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'EXPORT', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesExport', + 'operation' => operation, + } + + build_excon_response(body) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/get_instance.rb b/lib/fog/google/requests/sql/get_instance.rb new file mode 100644 index 000000000..2b84b2dee --- /dev/null +++ b/lib/fog/google/requests/sql/get_instance.rb @@ -0,0 +1,48 @@ +module Fog + module Google + class SQL + ## + # Retrieves a resource containing information about a Cloud SQL instance + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/get + + class Real + def get_instance(instance_id) + api_method = @sql.instances.get + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + request(api_method, parameters) + end + end + + class Mock + def get_instance(instance_id) + if self.data[:instances].has_key?(instance_id) + body = self.data[:instances][instance_id] + status = 200 + else + body = { + 'error' => { + 'errors' => [ + { + 'domain' => 'global', + 'reason' => 'notAuthorized', + 'message' => 'The client is not authorized to make this request.', + } + ], + 'code' => 403, + 'message' => 'The client is not authorized to make this request.', + } + } + status = 403 + end + + build_excon_response(body, status) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/import_instance.rb b/lib/fog/google/requests/sql/import_instance.rb new file mode 100644 index 000000000..f817b6b75 --- /dev/null +++ b/lib/fog/google/requests/sql/import_instance.rb @@ -0,0 +1,55 @@ +module Fog + module Google + class SQL + ## + # Imports data into a Cloud SQL instance from a MySQL dump file in Google Cloud Storage + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/import + + class Real + def import_instance(instance_id, uri, options = {}) + api_method = @sql.instances.import + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + body = { + 'importContext' => { + 'kind' => 'sql#importContext', + 'uri' => Array(uri), + 'database' => options[:database], + } + } + + request(api_method, parameters, body) + end + end + + class Mock + def import_instance(instance_id, uri, options = {}) + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'IMPORT', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesImport', + 'operation' => operation, + } + + build_excon_response(body) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/insert_instance.rb b/lib/fog/google/requests/sql/insert_instance.rb new file mode 100644 index 000000000..8daf54f20 --- /dev/null +++ b/lib/fog/google/requests/sql/insert_instance.rb @@ -0,0 +1,165 @@ +module Fog + module Google + class SQL + ## + # Creates a new Cloud SQL instance + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/insert + + class Real + def insert_instance(name, tier, options = {}) + api_method = @sql.instances.insert + parameters = { + 'project' => @project, + } + + body = { + 'project' => @project, + 'instance' => name, + 'settings' => { + 'tier' => tier, + } + } + + if options[:region] + body['region'] = options[:region] + end + if options[:activation_policy] + body['settings']['activationPolicy'] = options[:activation_policy] + end + if options[:autorized_gae_applications] + body['settings']['authorizedGaeApplications'] = Array(options[:autorized_gae_applications]) + end + if options[:backup_configuration] + body['settings']['backupConfiguration'] = options[:backup_configuration] + end + if options[:ip_configuration_authorized_networks] + body['settings']['ipConfiguration'] ||= {} + body['settings']['ipConfiguration']['authorizedNetworks'] = Array(options[:ip_configuration_authorized_networks]) + end + if options[:ip_configuration_enabled] + body['settings']['ipConfiguration'] ||= {} + body['settings']['ipConfiguration']['enabled'] = options[:ip_configuration_enabled] + end + if options[:ip_configuration_require_ssl] + body['settings']['ipConfiguration'] ||= {} + body['settings']['ipConfiguration']['requireSsl'] = options[:ip_configuration_require_ssl] + end + if options[:location_preference_zone_follow_gae_application] + body['settings']['locationPreference'] ||= {} + body['settings']['locationPreference']['followGaeApplication'] = options[:location_preference_zone_follow_gae_application] + end + if options[:location_preference_zone] + body['settings']['locationPreference'] ||= {} + body['settings']['locationPreference']['zone'] = options[:location_preference_zone] + end + if options[:pricing_plan] + body['settings']['pricingPlan'] = options[:pricing_plan] + end + if options[:replication_type] + body['settings']['replicationType'] = options[:replication_type] + end + + request(api_method, parameters, body) + end + end + + class Mock + def insert_instance(name, tier, options = {}) + data = { + 'kind' => 'sql#instance', + 'instance' => name, + 'etag' => Fog::Mock.random_base64(32), + 'project' => @project, + 'state' => Fog::Google::SQL::Instance::RUNNABLE_STATE , + 'databaseVersion' => 'MYSQL_5_5', + 'region' => options[:region] || 'us-central', + 'currentDiskSize' => '86245269', + 'maxDiskSize' => '268435456000', + 'settings' => { + 'kind' => 'sql#settings', + 'settingsVersion' => '1', + 'tier' => tier, + 'backupConfiguration' => [ + { + 'kind' => 'sql#backupConfiguration', + 'startTime' => '04:00', + 'enabled' => false, + 'id' => Fog::Mock.random_hex(32), + 'binaryLogEnabled' => false + } + ], + 'pricingPlan' => options[:pricing_plan] || 'PER_USE', + 'replicationType' => options[:replication_type] || 'SYNCHRONOUS', + 'activationPolicy' => options[:activation_policy] || 'ON_DEMAND', + 'ipConfiguration' => { + 'enabled' => false, + }, + 'locationPreference' => { + 'kind' => 'sql#locationPreference', + } + }, + 'serverCaCert' => { + 'kind' => 'sql#sslCert', + 'instance' => name, + 'sha1Fingerprint' => Fog::Mock.random_hex(40), + 'commonName' => 'C=US,O=Google\\, Inc,CN=Google Cloud SQL Server CA', + 'certSerialNumber' => '0', + 'cert' => "-----BEGIN CERTIFICATE-----\nMIIDITCCAgmgAwIBAgIBADANBgkqhkiG9w0BAQUFADBIMSMwIQYDVQQDExpHb29n\nbGUgQ2xvdWQgU1FMIFNlcnZlciBDQTEUMBIGA1UEChMLR29vZ2xlLCBJbmMxCzAJ\nBgNVBAYTAlVTMB4XDTE0MDYwNDA1MjkxMVoXDTI0MDYwMTA1MjkxMVowSDEjMCEG\nA1UEAxMaR29vZ2xlIENsb3VkIFNRTCBTZXJ2ZXIgQ0ExFDASBgNVBAoTC0dvb2ds\nZSwgSW5jMQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBALlRjq3zccH5ed6NMfCFcTYd9XxYXyvLurxxjDIA6A7/ymVM9qdQC0uckf7C\nsi4uMi2yfK+PHZ0jXC+g0uPx5RTm+nbKl4I++VOh2g6oZHeNdFt4rVJpr+jzGUMf\nr67SymUr70TQOTEVpx2Ud3rBB2szulxWUSXEy2AGA3uNUGe/IgENh7p56s00Sr97\nTRP1S5/JVMalncgNVLH2nNqBQJZTx9t9qvGatoUfmHUU0+M//J5sXLbgdzeEeeot\nHJUyoXjA2sRkH1+F/d6PpFrcr1I8dVmCBEbTAnm7HpKh5Mx2nRYi+t/y9D2Mblwx\n9dBRfr3WIJ1JDxzt3L8CtBGZbvUCAwEAAaMWMBQwEgYDVR0TAQH/BAgwBgEB/wIB\nADANBgkqhkiG9w0BAQUFAAOCAQEAmHuBecPc265sbd26B1HXUAD6FHdzoZLrAZVW\n+1eIK4E669P4y6LkLuoCkLd64/YwA4K2FioksqgHOahbYWJJYPymy4ae+IPXzXcY\nPv3gmBsKk++sHb64D9Cj/k5n8BEiVmmrsUCUiL75nJAzK+El3hvKKWWl76XX/qHP\nk8ZAxIrn8bCiVOaj6NR4+p1OmcZSPNWxz7j/EwQxoABRxgPgt+B/YRseevww7an2\n/rGs0sk7RE0QDjLfZblYGh+xVPBBLuLmf4L5JNJkFEoeGSWrxTzvXnS+2LZeHdM/\nJ9nsiKu5JKPhMUS0vOcTymOkh8tJ6Np8gwg6ca4g6dT3llE6uQ==\n-----END CERTIFICATE-----", + 'createTime' => Time.now.iso8601, + 'expirationTime' => Time.now.iso8601, + } + } + + if options[:autorized_gae_applications] + data['settings']['authorizedGaeApplications'] = Array(options[:autorized_gae_applications]) + end + if options[:backup_configuration] + data['settings']['backupConfiguration'] = options[:backup_configuration] + end + if options[:ip_configuration_authorized_networks] + data['settings']['ipConfiguration']['authorizedNetworks'] = Array(options[:ip_configuration_authorized_networks]) + end + if options[:ip_configuration_enabled] + data['settings']['ipConfiguration']['enabled'] = options[:ip_configuration_enabled] + end + if options[:ip_configuration_require_ssl] + data['settings']['ipConfiguration']['requireSsl'] = options[:ip_configuration_require_ssl] + end + if options[:location_preference_zone_follow_gae_application] + data['settings']['locationPreference']['followGaeApplication'] = options[:location_preference_zone_follow_gae_application] + end + if options[:location_preference_zone] + data['settings']['locationPreference']['zone'] = options[:location_preference_zone] + end + + self.data[:instances][name] = data + self.data[:ssl_certs][name] = {} + self.data[:backup_runs][name] = {} + + operation = self.random_operation + self.data[:operations][name] ||= {} + self.data[:operations][name][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => name, + 'operation' => operation, + 'operationType' => 'CREATE', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesInsert', + 'operation' => operation, + } + status = 200 + + build_excon_response(body, status) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/list_instances.rb b/lib/fog/google/requests/sql/list_instances.rb new file mode 100644 index 000000000..beb81d28f --- /dev/null +++ b/lib/fog/google/requests/sql/list_instances.rb @@ -0,0 +1,32 @@ +module Fog + module Google + class SQL + ## + # Lists instances under a given project in the alphabetical order of the instance name + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/list + + class Real + def list_instances + api_method = @sql.instances.list + parameters = { + 'project' => @project, + } + + request(api_method, parameters) + end + end + + class Mock + def list_instances + body = { + 'kind' => 'sql#instancesList', + 'items' => self.data[:instances].values, + } + + build_excon_response(body) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/reset_instance_ssl_config.rb b/lib/fog/google/requests/sql/reset_instance_ssl_config.rb new file mode 100644 index 000000000..bf39d8492 --- /dev/null +++ b/lib/fog/google/requests/sql/reset_instance_ssl_config.rb @@ -0,0 +1,49 @@ +module Fog + module Google + class SQL + ## + # Deletes all client certificates and generates a new server SSL certificate for the instance. + # The changes will not take effect until the instance is restarted. Existing instances without + # a server certificate will need to call this once to set a server certificate + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/resetSslConfig + + class Real + def reset_instance_ssl_config(instance_id) + api_method = @sql.instances.reset_ssl_config + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + request(api_method, parameters) + end + end + + class Mock + def reset_instance_ssl_config(instance_id) + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'UPDATE', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesResetSslConfig', + 'operation' => operation, + } + + build_excon_response(body) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/restart_instance.rb b/lib/fog/google/requests/sql/restart_instance.rb new file mode 100644 index 000000000..0cd77cd9e --- /dev/null +++ b/lib/fog/google/requests/sql/restart_instance.rb @@ -0,0 +1,47 @@ +module Fog + module Google + class SQL + ## + # Restarts a Cloud SQL instance + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/restart + + class Real + def restart_instance(instance_id) + api_method = @sql.instances.restart + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + request(api_method, parameters) + end + end + + class Mock + def restart_instance(instance_id) + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'RESTART', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesRestart', + 'operation' => operation, + } + + build_excon_response(body) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/restore_instance_backup.rb b/lib/fog/google/requests/sql/restore_instance_backup.rb new file mode 100644 index 000000000..73a3135cb --- /dev/null +++ b/lib/fog/google/requests/sql/restore_instance_backup.rb @@ -0,0 +1,30 @@ +module Fog + module Google + class SQL + ## + # Restores a backup of a Cloud SQL instance + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/restoreBackup + + class Real + def restore_instance_backup(identity, backup_configuration, due_time) + api_method = @sql.instances.reset_ssl_config + parameters = { + 'project' => @project, + 'instance' => identity, + 'backupConfiguration' => backup_configuration, + 'dueTime' => due_time, + } + + request(api_method, parameters) + end + end + + class Mock + def restore_instance_backup(identity, backup_configuration, due_time) + Fog::Mock.not_implemented + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/set_instance_root_password.rb b/lib/fog/google/requests/sql/set_instance_root_password.rb new file mode 100644 index 000000000..6be942605 --- /dev/null +++ b/lib/fog/google/requests/sql/set_instance_root_password.rb @@ -0,0 +1,54 @@ +module Fog + module Google + class SQL + ## + # Sets the password for the root user + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/setRootPassword + + class Real + def set_instance_root_password(instance_id, password) + api_method = @sql.instances.set_root_password + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + body = { + 'setRootPasswordContext' => { + 'kind' => 'sql#setRootUserContext', + 'password' => password, + } + } + + request(api_method, parameters, body) + end + end + + class Mock + def set_instance_root_password(instance_id, password) + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'INJECT_USER', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesSetRootPassword', + 'operation' => operation, + } + + build_excon_response(body) + end + end + end + end +end diff --git a/lib/fog/google/requests/sql/update_instance.rb b/lib/fog/google/requests/sql/update_instance.rb new file mode 100644 index 000000000..360188ca8 --- /dev/null +++ b/lib/fog/google/requests/sql/update_instance.rb @@ -0,0 +1,132 @@ +module Fog + module Google + class SQL + ## + # Updates settings of a Cloud SQL instance + # + # @see https://developers.google.com/cloud-sql/docs/admin-api/v1beta3/instances/update + + class Real + def update_instance(instance_id, settings_version, tier, options = {}) + api_method = @sql.instances.update + parameters = { + 'project' => @project, + 'instance' => instance_id, + } + + body = { + 'project' => @project, + 'instance' => instance_id, + 'settings' => { + 'settingsVersion' => settings_version, + 'tier' => tier, + } + } + + if options[:activation_policy] + body['settings']['activationPolicy'] = options[:activation_policy] + end + if options[:autorized_gae_applications] + body['settings']['authorizedGaeApplications'] = Array(options[:autorized_gae_applications]) + end + if options[:backup_configuration] + body['settings']['backupConfiguration'] = options[:backup_configuration] + end + if options[:ip_configuration_authorized_networks] + body['settings']['ipConfiguration'] ||= {} + body['settings']['ipConfiguration']['authorizedNetworks'] = Array(options[:ip_configuration_authorized_networks]) + end + if options[:ip_configuration_enabled] + body['settings']['ipConfiguration'] ||= {} + body['settings']['ipConfiguration']['enabled'] = options[:ip_configuration_enabled] + end + if options[:ip_configuration_require_ssl] + body['settings']['ipConfiguration'] ||= {} + body['settings']['ipConfiguration']['requireSsl'] = options[:ip_configuration_require_ssl] + end + if options[:location_preference_zone_follow_gae_application] + body['settings']['locationPreference'] ||= {} + body['settings']['locationPreference']['followGaeApplication'] = options[:location_preference_zone_follow_gae_application] + end + if options[:location_preference_zone] + body['settings']['locationPreference'] ||= {} + body['settings']['locationPreference']['zone'] = options[:location_preference_zone] + end + if options[:pricing_plan] + body['settings']['pricingPlan'] = options[:pricing_plan] + end + if options[:replication_type] + body['settings']['replicationType'] = options[:replication_type] + end + + request(api_method, parameters, body) + end + end + + class Mock + def update_instance(instance_id, settings_version, tier, options = {}) + data = self.data[:instances][instance_id] + data['tier'] = tier + if options[:activation_policy] + data['settings']['activationPolicy'] = options[:activation_policy] + end + if options[:autorized_gae_applications] + data['settings']['authorizedGaeApplications'] = Array(options[:autorized_gae_applications]) + end + if options[:backup_configuration] + data['settings']['backupConfiguration'] = options[:backup_configuration] + end + if options[:ip_configuration_authorized_networks] + data['settings']['ipConfiguration'] ||= {} + data['settings']['ipConfiguration']['authorizedNetworks'] = Array(options[:ip_configuration_authorized_networks]) + end + if options[:ip_configuration_enabled] + data['settings']['ipConfiguration'] ||= {} + data['settings']['ipConfiguration']['enabled'] = options[:ip_configuration_enabled] + end + if options[:ip_configuration_require_ssl] + data['settings']['ipConfiguration'] ||= {} + data['settings']['ipConfiguration']['requireSsl'] = options[:ip_configuration_require_ssl] + end + if options[:location_preference_zone_follow_gae_application] + data['settings']['locationPreference'] ||= {} + data['settings']['locationPreference']['followGaeApplication'] = options[:location_preference_zone_follow_gae_application] + end + if options[:location_preference_zone] + data['settings']['locationPreference'] ||= {} + data['settings']['locationPreference']['zone'] = options[:location_preference_zone] + end + if options[:pricing_plan] + data['settings']['pricingPlan'] = options[:pricing_plan] + end + if options[:replication_type] + data['settings']['replicationType'] = options[:replication_type] + end + self.data[:instances][instance_id] = data + + operation = self.random_operation + self.data[:operations][instance_id] ||= {} + self.data[:operations][instance_id][operation] = { + 'kind' => 'sql#instanceOperation', + 'instance' => instance_id, + 'operation' => operation, + 'operationType' => 'UPDATE', + 'state' => Fog::Google::SQL::Operation::DONE_STATE, + 'userEmailAddress' => 'google_client_email@developer.gserviceaccount.com', + 'enqueuedTime' => Time.now.iso8601, + 'startTime' => Time.now.iso8601, + 'endTime' => Time.now.iso8601, + } + + body = { + 'kind' => 'sql#instancesUpdate', + 'operation' => operation, + } + status = 200 + + build_excon_response(body, status) + end + end + end + end +end \ No newline at end of file diff --git a/lib/fog/google/sql.rb b/lib/fog/google/sql.rb index 179a52171..7c0dd2a07 100644 --- a/lib/fog/google/sql.rb +++ b/lib/fog/google/sql.rb @@ -20,6 +20,10 @@ module Fog model :flag collection :flags + # Instance + model :instance + collection :instances + # Operation model :operation collection :operations @@ -35,6 +39,20 @@ module Fog # Flag request :list_flags + # Instance + request :clone_instance + request :delete_instance + request :export_instance + request :get_instance + request :import_instance + request :insert_instance + request :list_instances + request :reset_instance_ssl_config + request :restart_instance + request :restore_instance_backup + request :set_instance_root_password + request :update_instance + # Operation request :get_operation request :list_operations diff --git a/tests/google/models/sql/instance_tests.rb b/tests/google/models/sql/instance_tests.rb new file mode 100644 index 000000000..8c6e7ed86 --- /dev/null +++ b/tests/google/models/sql/instance_tests.rb @@ -0,0 +1,56 @@ +Shindo.tests('Fog::Google[:sql] | instance model', ['google']) do + @instances = Fog::Google[:sql].instances + + tests('success') do + + tests('#create').succeeds do + @instance = @instances.create(:instance => Fog::Mock.random_letters(16), :tier => 'D1') + @instance.wait_for { ready? } + end + + tests('#update').succeeds do + @instance.activation_policy = 'ALWAYS' + @instance.update + @instance.wait_for { ready? } + end + + tests('#clone').succeeds do + pending unless Fog.mocking? # Binary log must be activated + instance_cloned_id = Fog::Mock.random_letters(16) + @instance.clone(instance_cloned_id, :async => false) + @instances.get(instance_cloned_id).destroy + end + + tests('#export').succeeds do + pending unless Fog.mocking? # We don't have access to a Google Cloud Storage bucket + @instance.export("gs://#{Fog::Mock.random_letters_and_numbers(16)}/mysql-export", :async => false) + end + + tests('#import').succeeds do + pending unless Fog.mocking? # We don't have access to a Google Cloud Storage bucket + @instance.import("gs://#{Fog::Mock.random_letters_and_numbers(16)}/mysql-export", :async => false) + end + + tests('#ready?').succeeds do + @instance.ready? == true + end + + tests('#reset_ssl_config').succeeds do + @instance.reset_ssl_config(:async => false) + end + + tests('#restart').succeeds do + @instance.restart(:async => false) + end + + tests('#set_root_password').succeeds do + @instance.set_root_password(Fog::Mock.random_letters_and_numbers(8), :async => false) + end + + tests('#destroy').succeeds do + @instance.destroy + end + + end + +end diff --git a/tests/google/models/sql/instances_tests.rb b/tests/google/models/sql/instances_tests.rb new file mode 100644 index 000000000..abcb9f65d --- /dev/null +++ b/tests/google/models/sql/instances_tests.rb @@ -0,0 +1,32 @@ +Shindo.tests('Fog::Google[:sql] | instances model', ['google']) do + @instance = Fog::Google[:sql].instances.create(:instance => Fog::Mock.random_letters(16), :tier => 'D1') + @instance.wait_for { ready? } + @instances = Fog::Google[:sql].instances + + tests('success') do + + tests('#all').succeeds do + @instances.all + end + + tests('#get').succeeds do + @instances.get(@instance.instance) + end + + end + + @instance.destroy(:async => false) + + tests('failure') do + + tests('#all').returns([]) do + @instances.all + end + + tests('#get').returns(nil) do + @instances.get(Fog::Mock.random_letters_and_numbers(16)) + end + + end + +end diff --git a/tests/google/requests/sql/instance_tests.rb b/tests/google/requests/sql/instance_tests.rb new file mode 100644 index 000000000..0172dde9c --- /dev/null +++ b/tests/google/requests/sql/instance_tests.rb @@ -0,0 +1,129 @@ +Shindo.tests('Fog::Google[:sql] | instance requests', ['google']) do + @sql = Fog::Google[:sql] + @instance_id = Fog::Mock.random_letters(16) + + @insert_instance_format = { + 'kind' => String, + 'operation' => String, + } + + @get_instance_format = { + 'instance' => String, + 'currentDiskSize' => Fog::Nullable::String, + 'databaseVersion' => String, + 'etag' => String, + 'ipAddresses' => Fog::Nullable::String, + 'kind' => String, + 'maxDiskSize' => String, + 'project' => String, + 'region' => String, + 'serverCaCert' => Hash, + 'settings' => Hash, + 'state' => String, + } + + @list_instances_format = { + 'kind' => String, + 'items' => [@get_instance_format], + } + + @clone_instance_format = { + 'kind' => String, + 'operation' => String, + } + + @export_instance_format = { + 'kind' => String, + 'operation' => String, + } + + @import_instance_format = { + 'kind' => String, + 'operation' => String, + } + + @reset_instance_ssl_config_format = { + 'kind' => String, + 'operation' => String, + } + + @restart_instance_format = { + 'kind' => String, + 'operation' => String, + } + + @set_instance_root_password_format = { + 'kind' => String, + 'operation' => String, + } + + @update_instance_format = { + 'kind' => String, + 'operation' => String, + } + + @delete_instance_format = { + 'kind' => String, + 'operation' => String, + } + + tests('success') do + + tests('#insert_instance').formats(@insert_instance_format) do + result = @sql.insert_instance(@instance_id, 'D1').body + @sql.instances.get(@instance_id).wait_for { ready? } + result + end + + tests('#list_instances').formats(@list_instances_format) do + @sql.list_instances.body + end + + tests('#get_instance').formats(@get_instance_format) do + @sql.get_instance(@instance_id).body + end + + tests('#update_instance').formats(@update_instance_format) do + instance = @sql.instances.get(@instance_id) + @sql.update_instance(instance.instance, instance.settings_version, instance.tier, instance.attributes).body + end + + tests('#clone_instance').formats(@clone_instance_format) do + pending unless Fog.mocking? # Binary log must be activated + instance_cloned_id = Fog::Mock.random_letters(16) + clone_instance_format = @sql.clone_instance(@instance_id, instance_cloned_id).body + @sql.delete_instance(instance_cloned_id) + clone_instance_format + end + + tests('#export_instance').formats(@export_instance_format) do + pending unless Fog.mocking? # We don't have access to a Google Cloud Storage bucket + @sql.export_instance(@instance_id, "gs://#{Fog::Mock.random_letters_and_numbers(16)}/mysql-export").body + end + + tests('#import_instance').formats(@import_instance_format) do + pending unless Fog.mocking? # We don't have access to a Google Cloud Storage bucket + @sql.import_instance(@instance_id, "gs://#{Fog::Mock.random_letters_and_numbers(16)}/mysql-export").body + end + + tests('#reset_instance_ssl_config').formats(@reset_instance_ssl_config_format) do + @sql.reset_instance_ssl_config(@instance_id).body + end + + tests('#set_instance_root_password').formats(@set_instance_root_password_format) do + @sql.set_instance_root_password(@instance_id, Fog::Mock.random_letters_and_numbers(8)).body + end + + tests('#restart_instance').formats(@restart_instance_format) do + result = @sql.restart_instance(@instance_id).body + @sql.operations.get(@instance_id, result['operation']).wait_for { ready? } + result + end + + tests('#delete_instance').formats(@delete_instance_format) do + @sql.delete_instance(@instance_id).body + end + + end + +end