diff --git a/README.md b/README.md index 83d28ff27..f07290b94 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,10 @@ There are two ways you can set up the connections: config.management_certificate = "" config.subscription_id = "" config.management_endpoint = "https://management.core.windows.net" + # This property enables/disables SQL API RDFE endpoint. By default RDFE is enabled. RDFE will also be enabled if you do not set this property + config.disable_sql_rdfe = true + # Configure the non-RDFE SQL API endpoint here + config.sql_management_endpoint = "http://management.database.windows.net:8443" end ``` * Against local Emulator (Windows Only) @@ -146,7 +150,8 @@ There are two ways you can set up the connections: AZURE_MANAGEMENT_CERTIFICATE = AZURE_SUBSCRIPTION_ID = AZURE_MANAGEMENT_ENDPOINT = - + AZURE_SQL_MANAGEMENT_ENDPOINT = + AZURE_DISABLE_SQL_RDFE = ``` * Against local Emulator (Windows Only) * Storage diff --git a/Rakefile b/Rakefile index 69f8a3c8c..73dfce584 100644 --- a/Rakefile +++ b/Rakefile @@ -89,16 +89,17 @@ namespace :test do task component => "test:require_environment" end - component_task :blob - component_task :queue - component_task :table - component_task :service_bus - component_task :database component_task :affinity_group + component_task :blob + component_task :database component_task :location - component_task :vnet + component_task :queue + component_task :service_bus + component_task :storage_management + component_task :table component_task :vm component_task :vm_image + component_task :vnet end task :cleanup => :require_environment do @@ -117,6 +118,7 @@ namespace :test do # config.sb_issuer = ENV.fetch("AZURE_SERVICEBUS_ISSUER") config.management_certificate = ENV.fetch('AZURE_MANAGEMENT_CERTIFICATE') config.management_endpoint = ENV.fetch("AZURE_MANAGEMENT_ENDPOINT") + config.sql_management_endpoint = ENV.fetch("AZURE_SQL_MANAGEMENT_ENDPOINT") config.subscription_id = ENV.fetch("AZURE_SUBSCRIPTION_ID") end end diff --git a/lib/azure.rb b/lib/azure.rb index a243c6d4b..ae7d2ccde 100644 --- a/lib/azure.rb +++ b/lib/azure.rb @@ -28,6 +28,7 @@ require 'azure/table/batch' require 'azure/table/query' require 'azure/core/utility' require 'azure/base_management/management_http_request' +require 'azure/base_management/sql_management_http_request' require 'azure/base_management/base_management_service' require 'azure/virtual_machine_image_management/virtual_machine_image_management_service' require 'azure/virtual_machine_management/virtual_machine_management_service' diff --git a/lib/azure/base_management/sql_management_http_request.rb b/lib/azure/base_management/sql_management_http_request.rb new file mode 100644 index 000000000..10a31ecb6 --- /dev/null +++ b/lib/azure/base_management/sql_management_http_request.rb @@ -0,0 +1,37 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#-------------------------------------------------------------------------- +# Represents an HTTP request that can perform synchronous queries to +# an HTTP server, returning a HttpResponse +module Azure + module BaseManagement + # This class is used for communicating with the non-RDFE SQL API endpoint + class SqlManagementHttpRequest < ManagementHttpRequest + attr_accessor :uri + # Public: Creates the ManagementHttpRequest + # + # method - Symbol. The HTTP method to use (:get, :post, :put, :del, etc...) + # path - URI. The URI of the HTTP endpoint to query + # body - IO or String. The request body (optional) + # key - String. The request key + # cert - String. The request certificate + def initialize(method, path, body=nil) + super(method, path, body) + @uri = URI.parse(Azure.config.sql_management_endpoint + Azure.config.subscription_id + path) + @key = Azure.config.http_private_key + @cert = Azure.config.http_certificate_key + end + end + end +end diff --git a/lib/azure/cloud_service_management/cloud_service.rb b/lib/azure/cloud_service_management/cloud_service.rb index c8be4cd49..4802b11b1 100644 --- a/lib/azure/cloud_service_management/cloud_service.rb +++ b/lib/azure/cloud_service_management/cloud_service.rb @@ -21,11 +21,18 @@ module Azure yield self if block_given? end + attr_accessor :url attr_accessor :name + attr_accessor :label attr_accessor :description attr_accessor :location attr_accessor :affinity_group + attr_accessor :status + attr_accessor :date_created + attr_accessor :date_modified + attr_accessor :extended_properties + attr_accessor :default_winrm_certificate_thumbprint end end -end \ No newline at end of file +end diff --git a/lib/azure/cloud_service_management/cloud_service_management_service.rb b/lib/azure/cloud_service_management/cloud_service_management_service.rb index e299f58df..fbff2c4dd 100644 --- a/lib/azure/cloud_service_management/cloud_service_management_service.rb +++ b/lib/azure/cloud_service_management/cloud_service_management_service.rb @@ -32,13 +32,22 @@ module Azure # ==== Options # # Accepted key/value pairs in options parameter are: - # * +:location+ - String. The regional data center location where the cloud service will be created.(optional) + # * +:label+ -String. The label for this cloud service. # * +:description+ - String. A description for the hosted service. (optional) + # * +:location+ - String. The regional data center location where the + # cloud service will be created. Required if affinity group not + # specified (optional) + # * +:affinity_group_name - String. Name of the affinity group with + # which to assocate the cloud service. Required if location not + # specified (optional) + # * +:extended_properties+ - Hash. Key/Value pairs of extended + # properties to add to the cloud service. The key is used as the + # property name and the value as its value. (optional) # # See http://msdn.microsoft.com/en-us/library/windowsazure/gg441304.aspx # # Returns None - def create_cloud_service(name, options={}) + def create_cloud_service(name, options = {}) Loggerx.error_with_exit "Cloud service name is not valid " unless name if get_cloud_service(name) Loggerx.warn "Cloud service #{name} already exists. Skipped..." diff --git a/lib/azure/cloud_service_management/serialization.rb b/lib/azure/cloud_service_management/serialization.rb index 45e3470af..24a5aa793 100644 --- a/lib/azure/cloud_service_management/serialization.rb +++ b/lib/azure/cloud_service_management/serialization.rb @@ -18,30 +18,68 @@ require 'azure/cloud_service_management/cloud_service' module Azure module CloudServiceManagement module Serialization + def self.cloud_services_to_xml(name, options = {}) + options[:label] = options[:label] || name - def self.cloud_services_to_xml(name, options={}) builder = Nokogiri::XML::Builder.new do |xml| - xml.CreateHostedService('xmlns'=>'http://schemas.microsoft.com/windowsazure') { - xml.ServiceName name - xml.Label Base64.encode64(name) - xml.Description options[:description] || 'Explicitly created cloud service' - if !options[:affinity_group_name].nil? - xml.AffinityGroup options[:affinity_group_name] + xml.CreateHostedService( + 'xmlns' => 'http://schemas.microsoft.com/windowsazure' + ) do + xml.ServiceName(name) + xml.Label(Base64.encode64(options[:label])) + xml.Description(options[:description]) unless\ + options[:description].nil? || options[:description].empty? + + unless options[:affinity_group_name].nil? + xml.AffinityGroup(options[:affinity_group_name]) else - xml.Location options[:location] + xml.Location(options[:location]) end - } + + xml.ExtendedProperties do + options[:extended_properties].each do |prop_name, prop_value| + xml.ExtendedProperty do + xml.Name(prop_name) + xml.Value(prop_value) + end unless (prop_name.nil? || prop_name.empty?)\ + || (prop_value.nil? || prop_value.empty?) + end + end unless options[:extended_properties].nil?\ + || options[:extended_properties].empty? + end end builder.doc.to_xml end - - def self.cloud_services_from_xml(cloudXML) + + def self.cloud_services_from_xml(cloud_xml) clouds = [] - cloud_services_xml = cloudXML.css('HostedServices HostedService') + cloud_services_xml = cloud_xml.css('HostedServices HostedService') cloud_services_xml.each do |cloud_service_xml| cloud = CloudService.new + cloud.url = xml_content(cloud_service_xml, 'Url') cloud.name = xml_content(cloud_service_xml, 'ServiceName') - cloud.affinity_group = xml_content(cloud_service_xml, 'HostedServiceProperties AffinityGroup') + + props_xml = cloud_service_xml.css('HostedServiceProperties') + + cloud.label = Base64.decode64(xml_content(props_xml, 'Label')) + cloud.description = xml_content(props_xml, 'Description') + cloud.location = xml_content(props_xml, 'Location') + cloud.affinity_group = xml_content(props_xml, 'AffinityGroup') + cloud.status = xml_content(props_xml, 'Status') + cloud.date_created = xml_content(props_xml, 'DateCreated') + cloud.date_modified = xml_content(props_xml, 'DateLastModified') + + cloud.extended_properties = {} + props_xml.css('ExtendedProperties ExtendedProperty').map do |prop| + p_name = xml_content(prop, 'Name') + p_value = xml_content(prop, 'Value') + cloud.extended_properties[p_name] = p_value + end + + cloud.default_winrm_certificate_thumbprint = xml_content( + cloud_service_xml, 'DefaultWinRMCertificateThumbprint' + ) + clouds << cloud end clouds.compact diff --git a/lib/azure/core.rb b/lib/azure/core.rb index 0465abc90..0b4b44ed8 100644 --- a/lib/azure/core.rb +++ b/lib/azure/core.rb @@ -18,7 +18,8 @@ module Azure::Core; end require "azure/core/error" require "azure/core/configuration" -# load default configuration from environment variables (user can always override this later) +# load default configuration from environment variables +# (user can always override this later) Azure.configure do |config| config.storage_access_key = ENV["AZURE_STORAGE_ACCESS_KEY"] config.storage_account_name = ENV["AZURE_STORAGE_ACCOUNT"] @@ -33,4 +34,7 @@ Azure.configure do |config| config.management_certificate = ENV["AZURE_MANAGEMENT_CERTIFICATE"] config.subscription_id = ENV["AZURE_SUBSCRIPTION_ID"] config.management_endpoint = ENV["AZURE_MANAGEMENT_ENDPOINT"] -end \ No newline at end of file + config.sql_management_endpoint = ENV["AZURE_SQL_MANAGEMENT_ENDPOINT"] + + config.disable_sql_rdfe = ENV['AZURE_DISABLE_SQL_RDFE'] || 'false' +end diff --git a/lib/azure/core/configuration.rb b/lib/azure/core/configuration.rb index fd866d7dc..204957861 100644 --- a/lib/azure/core/configuration.rb +++ b/lib/azure/core/configuration.rb @@ -167,6 +167,9 @@ module Azure #Public: Set the certificate key for SSL/HTTPS request with PEM certificate attr_accessor :http_certificate_key + # Public: Set the host for SQL Management API (non-RDFE SQL Endpoint) + attr_accessor :sql_management_endpoint + def management_endpoint if @management_endpoint.nil? or @management_endpoint.empty? "https://management.core.windows.net/" @@ -179,6 +182,25 @@ module Azure end end + def sql_management_endpoint + if @sql_management_endpoint.nil? or @sql_management_endpoint.empty? + "https://management.database.windows.net:8443/" + elsif !@sql_management_endpoint.end_with?('/') + @sql_management_endpoint += '/' + elsif URI(@sql_management_endpoint).scheme.nil? + "https://#{@sql_management_endpoint}" + else + @management_endpoint + end + end + + # Public: Set this true to use non-RDFE SQL API Endpoint + attr_accessor :disable_sql_rdfe + + def disable_sql_rdfe + return false if @disable_sql_rdfe.nil? or @disable_sql_rdfe.empty? + @disable_sql_rdfe.downcase == 'true' + end end end end diff --git a/lib/azure/sql_database_management/serialization.rb b/lib/azure/sql_database_management/serialization.rb index 47c68b5cf..cfa6244d1 100644 --- a/lib/azure/sql_database_management/serialization.rb +++ b/lib/azure/sql_database_management/serialization.rb @@ -61,6 +61,9 @@ module Azure end def self.firewall_rule_to_xml(options) + # Need to revisit and implement RDFE request XML. + # Currently Azure is throwing Internal Server Error when executing the + # API builder = Nokogiri::XML::Builder.new do |xml| xml.FirewallRule('xmlns'=>'http://schemas.microsoft.com/sqlazure/2010/12/', 'xmlns:xsi'=>'http://www.w3.org/2001/XMLSchema-instance', @@ -74,13 +77,31 @@ module Azure def self.database_firewall_from_xml(response_xml) firewalls = [] - database_firewallXML = response_xml.css('FirewallRules FirewallRule') - database_firewallXML.each do |firewall_xml| - firewall = { :rule => xml_content(firewall_xml, 'Name'), - :start_ip_address => xml_content(firewall_xml, 'StartIpAddress'), - :end_ip_address => xml_content(firewall_xml, 'EndIpAddress') - } - firewalls << firewall + if Azure.config.disable_sql_rdfe + database_firewallXML = response_xml.css('FirewallRules FirewallRule') + database_firewallXML.each do |firewall_xml| + firewall = { + :rule => xml_content(firewall_xml, 'Name'), + :start_ip_address => xml_content(firewall_xml, 'StartIpAddress'), + :end_ip_address => xml_content(firewall_xml, 'EndIpAddress') + } + firewalls << firewall + end + else + service_resources = response_xml.css( + 'ServiceResources ServiceResource' + ) + service_resources.each do |resource| + type = xml_content(resource, 'Type') + if type == 'Microsoft.SqlAzure.FirewallRule' + firewall = { + rule: xml_content(resource, 'Name'), + start_ip_address: xml_content(resource, 'StartIPAddress'), + end_ip_address: xml_content(resource, 'EndIPAddress') + } + firewalls << firewall + end + end end firewalls.compact end diff --git a/lib/azure/sql_database_management/sql_database_management_service.rb b/lib/azure/sql_database_management/sql_database_management_service.rb index 20d87058a..2ec80cba5 100644 --- a/lib/azure/sql_database_management/sql_database_management_service.rb +++ b/lib/azure/sql_database_management/sql_database_management_service.rb @@ -31,8 +31,15 @@ module Azure # Returns an array of Azure::SqlDatabaseManagement::SqlDatabase objects def list_servers request_path = "/servers" - request = ManagementHttpRequest.new(:get, request_path, nil) - request.headers["x-ms-version"] = @x_ms_version + + if Azure.config.disable_sql_rdfe + request = SqlManagementHttpRequest.new(:get, request_path, nil) + request.headers["x-ms-version"] = @x_ms_version + else + request_path = "/services/sqlservers#{request_path}" + request = ManagementHttpRequest.new(:get, request_path, nil) + end + response = request.call Serialization.databases_from_xml(response) end @@ -51,8 +58,15 @@ module Azure def create_server(login, password, location) body = Serialization.database_to_xml(login, password, location) request_path = "/servers" - request = ManagementHttpRequest.new(:post, request_path, body) - request.headers["x-ms-version"] = @x_ms_version + + if Azure.config.disable_sql_rdfe + request = SqlManagementHttpRequest.new(:post, request_path, body) + request.headers["x-ms-version"] = @x_ms_version + else + request_path = "/services/sqlservers#{request_path}" + request = ManagementHttpRequest.new(:post, request_path, body) + end + response = request.call sql_server = Serialization.server_name_from_xml(response, login, location) Loggerx.info "SQL database server #{sql_server.name} is created." if sql_server @@ -72,8 +86,15 @@ module Azure def delete_server(name) if get_sql_server(name) request_path = "/servers/#{name}" - request = ManagementHttpRequest.new(:delete, request_path) - request.headers["x-ms-version"] = @x_ms_version + + if Azure.config.disable_sql_rdfe + request = SqlManagementHttpRequest.new(:delete, request_path) + request.headers["x-ms-version"] = @x_ms_version + else + request_path = "/services/sqlservers#{request_path}" + request = ManagementHttpRequest.new(:delete, request_path) + end + request.call Loggerx.info "Deleted database server #{name}." end @@ -94,8 +115,15 @@ module Azure if get_sql_server(name) request_path = "/servers/#{name}?op=ResetPassword" body = Serialization.reset_password_to_xml(password) - request = ManagementHttpRequest.new(:post, request_path, body) - request.headers["x-ms-version"] = @x_ms_version + + if Azure.config.disable_sql_rdfe + request = SqlManagementHttpRequest.new(:post, request_path, body) + request.headers["x-ms-version"] = @x_ms_version + else + request_path = "/services/sqlservers#{request_path}" + request = ManagementHttpRequest.new(:post, request_path, body) + end + request.call Loggerx.info "Password for server #{name} changed successfully." end @@ -132,8 +160,12 @@ module Azure request_path = "/servers/#{server_name}/firewallrules/#{rule_name}?op=AutoDetectClientIP" method = :post end - request = ManagementHttpRequest.new(method, request_path, body) + request = SqlManagementHttpRequest.new(method, request_path, body) request.headers["x-ms-version"] = @x_ms_version + + # RDFE Endpoint throws errors for this operation. Need to re-visit + # this once the Azure API is working. + request.call Loggerx.info "Added server-level firewall rule #{rule_name}." end @@ -152,8 +184,15 @@ module Azure def list_sql_server_firewall_rules(server_name) if get_sql_server(server_name) request_path = "/servers/#{server_name}/firewallrules" - request = ManagementHttpRequest.new(:get, request_path) - request.headers["x-ms-version"] = @x_ms_version + + if Azure.config.disable_sql_rdfe + request = SqlManagementHttpRequest.new(:get, request_path) + request.headers["x-ms-version"] = @x_ms_version + else + request_path = "/services/sqlservers#{request_path}" + request = ManagementHttpRequest.new(:get, request_path) + end + response = request.call Serialization.database_firewall_from_xml(response) end @@ -175,8 +214,15 @@ module Azure raise error elsif get_sql_server(server_name) request_path = "/servers/#{server_name}/firewallrules/#{rule_name}" - request = ManagementHttpRequest.new(:delete, request_path) - request.headers["x-ms-version"] = @x_ms_version + + if Azure.config.disable_sql_rdfe + request = SqlManagementHttpRequest.new(:delete, request_path) + request.headers["x-ms-version"] = @x_ms_version + else + request_path = "/services/sqlservers#{request_path}" + request = ManagementHttpRequest.new(:delete, request_path) + end + request.call Loggerx.info "Deleted server-level firewall rule #{rule_name}." end diff --git a/lib/azure/storage_management/serialization.rb b/lib/azure/storage_management/serialization.rb index 1a82a636c..fa8c21e62 100644 --- a/lib/azure/storage_management/serialization.rb +++ b/lib/azure/storage_management/serialization.rb @@ -17,34 +17,174 @@ require 'azure/storage_management/storage_account' module Azure module StorageManagement + # Storage management serialization module is responsible for converting + # the objects to XML and vice versa. module Serialization - def self.storage_services_to_xml(name, options={}) + def self.storage_services_to_xml(name, options = {}) builder = Nokogiri::XML::Builder.new do |xml| - xml.CreateStorageServiceInput('xmlns'=>'http://schemas.microsoft.com/windowsazure') { - xml.ServiceName name - xml.Label Base64.encode64(name) - xml.Description options[:description] || 'Explicitly created storage service' - if !options[:affinity_group_name].nil? + xml.CreateStorageServiceInput( + 'xmlns' => 'http://schemas.microsoft.com/windowsazure' + ) do + xml.ServiceName(name) + label = options[:label] || name + xml.Label(Base64.encode64(label)) + xml.Description options[:description]\ + || 'Explicitly created storage service' + unless options[:affinity_group_name].nil? xml.AffinityGroup options[:affinity_group_name] else xml.Location options[:location] end - } + add_options_to_xml(xml, options) + end end builder.doc.to_xml end - - def self.storage_services_from_xml(storageXML) + + def self.storage_services_from_xml(storage_xml) storage_accounts = [] - storage_servicesXML = storageXML.css('StorageServices StorageService') - storage_servicesXML.each do |storage_service_xml| + storage_services_xml = storage_xml.css('StorageService') + storage_services_xml.each do |storage_service_xml| storage_account = StorageAccount.new - storage_account.name = xml_content(storage_service_xml, 'ServiceName') + + storage_account.url = xml_content(storage_service_xml, 'Url') + storage_account.name = xml_content( + storage_service_xml, 'ServiceName' + ) + + storage_service_properties = storage_service_xml.css( + 'StorageServiceProperties' + ) + + storage_account.description = xml_content( + storage_service_properties, 'Description' + ) + storage_account.affinity_group = xml_content( + storage_service_properties, 'AffinityGroup' + ) + storage_account.location = xml_content( + storage_service_properties, 'Location' + ) + storage_account.label = Base64.decode64( + xml_content(storage_service_properties, 'Label') + ) + storage_account.status = xml_content( + storage_service_properties, 'Status' + ) + storage_account.endpoints = storage_service_properties.css( + 'Endpoints Endpoint' + ).map { |endpoint| endpoint.content } + storage_account.geo_replication_enabled = xml_content( + storage_service_properties, 'GeoReplicationEnabled' + ) + storage_account.geo_primary_region = xml_content( + storage_service_properties, 'GeoPrimaryRegion' + ) + storage_account.status_of_primary = xml_content( + storage_service_properties, 'StatusOfPrimary' + ) + storage_account.last_geo_failover_time = xml_content( + storage_service_properties, 'LastGeoFailoverTime' + ) + storage_account.geo_secondary_region = xml_content( + storage_service_properties, 'GeoSecondaryRegion' + ) + storage_account.status_of_secondary = xml_content( + storage_service_properties, 'StatusOfSecondary' + ) + storage_account.creation_time = xml_content( + storage_service_properties, 'CreationTime' + ) + storage_account.extended_properties = storage_service_xml.css( + 'ExtendedProperties ExtendedProperty' + ).map do |prop| + { + name: xml_content(prop, 'Name'), + value: xml_content(prop, 'Value') + } + end + storage_accounts << storage_account end + + # returns the first storage account if only one found + # This will be the case when calling the get_storage_account_properties + # API or if only one storage account exists for the subscription + return storage_accounts.first if storage_accounts.size == 1 + + # returns all the storage accounts, if more than 1 found storage_accounts.compact end + def self.storage_update_to_xml(options) + # Cannot update if options is nil or empty + fail 'No options specified' if options.nil? || options.empty? + + # Either one of Label, or Description is required. + if (options[:label].nil? || options[:label].empty?) && + (options[:description].nil? || options[:description].empty?) + fail 'Either one of Label or Description'\ + ' has to be provided. Both cannot be empty' + end + + # The input param may not be nil or empty, but the values inside may + # be. Check if atleast one value exists before proceeding. + is_empty = true + options.each do |option, value| + case option + when :description, :label + is_empty = value.nil? || value.empty? + when :geo_replication_enabled + is_empty = !(value.kind_of?(TrueClass)\ + || value.kind_of?(FalseClass)) + when :extended_properties + value.each do |p, v| + is_empty = ((p.nil? || p.empty?) || (v.nil? || v.empty?)) + break unless is_empty + end + end + + break unless is_empty + end + + # Raise a RuntimeError if no options were provided + fail 'No Options Specified' if is_empty + + builder = Nokogiri::XML::Builder.new do |xml| + xml.UpdateStorageServiceInput( + 'xmlns' => 'http://schemas.microsoft.com/windowsazure' + ) do + # Check if label is nil. Use description only if label is nil + if options[:label].nil? || options[:label].empty? + desc = options[:description] + xml.Description(desc) unless desc.nil? || desc.empty? + else + label = Base64.encode64(options[:label]) + xml.Label(label) unless label.nil? || label.empty? + end + add_options_to_xml(xml, options) + end + end + builder.to_xml + end + + def self.add_options_to_xml(xml, options = {}) + gre = options[:geo_replication_enabled] + xml.GeoReplicationEnabled( + gre + ) unless gre.nil?\ + || !(gre.kind_of?(TrueClass) || gre.kind_of?(FalseClass)) + xml.ExtendedProperties do + options[:extended_properties].each do |name, value| + xml.ExtendedProperty do + xml.Name name + xml.Value value + end unless (name.nil? || name.empty?)\ + || (value.nil? || value.empty?) + end + end unless options[:extended_properties].nil?\ + || options[:extended_properties].empty? + end end end -end \ No newline at end of file +end diff --git a/lib/azure/storage_management/storage_account.rb b/lib/azure/storage_management/storage_account.rb index 69b04bf37..e85d56493 100644 --- a/lib/azure/storage_management/storage_account.rb +++ b/lib/azure/storage_management/storage_account.rb @@ -14,16 +14,27 @@ #-------------------------------------------------------------------------- module Azure module StorageManagement + # Represents a Windows Azure storage account class StorageAccount - def initialize yield self if block_given? end - + attr_accessor :url attr_accessor :name attr_accessor :description attr_accessor :location - + attr_accessor :affinity_group + attr_accessor :label + attr_accessor :status + attr_accessor :endpoints + attr_accessor :geo_replication_enabled + attr_accessor :geo_primary_region + attr_accessor :status_of_primary + attr_accessor :last_geo_failover_time + attr_accessor :geo_secondary_region + attr_accessor :status_of_secondary + attr_accessor :creation_time + attr_accessor :extended_properties end end -end \ No newline at end of file +end diff --git a/lib/azure/storage_management/storage_management_service.rb b/lib/azure/storage_management/storage_management_service.rb index 840d20778..6b5f6e57a 100644 --- a/lib/azure/storage_management/storage_management_service.rb +++ b/lib/azure/storage_management/storage_management_service.rb @@ -16,17 +16,17 @@ require 'azure/storage_management/serialization' module Azure module StorageManagement + # Provides Storage Management API class StorageManagementService < BaseManagementService - def initialize super() end - - # Public: Gets a list of storage accounts available under the current subscription. + # Public: Gets a list of storage accounts available under the + # current subscription. # # Returns an array of Azure::StorageManagement::StorageAccount objects def list_storage_accounts - request_path = "/services/storageservices" + request_path = '/services/storageservices' request = ManagementHttpRequest.new(:get, request_path, nil) response = request.call Serialization.storage_services_from_xml(response) @@ -39,11 +39,12 @@ module Azure # * +name+ - String. Storage account name. # # Returns: A boolean value indicating whether the storage account exists. - # If true, the storage account exists. If false, the storage account does not exist. + # If true, the storage account exists. If false, the storage account + # does not exist. def get_storage_account(name) return false if name.nil? flag = false - storage_accounts = Azure::StorageManagementService.new.list_storage_accounts + storage_accounts = list_storage_accounts storage_accounts.each do |storage| if storage.name == name flag = true @@ -53,6 +54,22 @@ module Azure flag end + # Public: Gets the properties of the storage account specified. + # + # ==== Attributes + # + # * +name+ - String. The name of the storage account. Required. + # + # See http://msdn.microsoft.com/en-us/library/windowsazure/ee460802.aspx + # + # Returns the storage account + def get_storage_account_properties(name) + request_path = "/services/storageservices/#{name}" + request = ManagementHttpRequest.new(:get, request_path, nil) + response = request.call + Serialization.storage_services_from_xml(response) + end + # Public: Create a new storage account in Windows Azure. # # ==== Attributes @@ -63,23 +80,73 @@ module Azure # ==== Options # # Accepted key/value pairs in options parameter are: - # * +:location+ - String. The location where the storage service will be created.(optional) - # * +:description+ - String. A description for the storage service. (optional) + # * +:label+ - String. The label for this storage account. The name will + # be used as label if none specified. (optional) + # * +:location+ - String. The location where the storage + # service will be created. Reqruied if no affinity_group_name specified. + # * +:description+ - String. A description for the storage + # service. (optional) + # * +:affinity_group_name+ - String. The name of an existing affinity group + # in the specified subscription. Required if no location specified. + # * +:geo_replication_enabled+ - String. A flag indicating wheter to + # turn Geo replication on or off. Values other than 'true'/'false' + # will result in an error from the REST API. (optional) + # * +:extended_properties+ - Hash. Key/Value pairs of extended + # properties to add to the storage account. The key is used as the + # property name and the value as its value. (optional) # # Returns None - def create_storage_account(name, options={}) + def create_storage_account(name, options = {}) + raise 'Name not specified' if !name || name.class != String || name.empty? if get_storage_account(name) Loggerx.warn "Storage Account #{name} already exists. Skipped..." else Loggerx.info "Creating Storage Account #{name}." body = Serialization.storage_services_to_xml(name, options) - request_path = "/services/storageservices" + request_path = '/services/storageservices' request = ManagementHttpRequest.new(:post, request_path, body) request.call end end - # Public: Deletes the specified storage account of given subscription id from Windows Azure. + # Public: Updates an existing storage account in Windows Azure + # + # ==== Attributes + # + # * +name+ - String. The name of the storage service. + # * +options+ - Hash. Optional parameters. + # + # ==== Options + # + # Accepted key/value pairs in options parameter are: + # * +:label+ - String. A label for the storage service. Required if no + # description is provided. If both label and description are + # provided, only the label will get updated. + # * +:description+ - String. A description for the storage service. + # Required if no label is provided. If both label and description are + # provided, only the label will get updated. + # * +:geo_replication_enabled+ - Boolean (TrueClass/FalseClass). Boolean + # flag indicating wheter to turn Geo replication on or off. (optional) + # * +:extended_properties+ - Hash. Key/Value pairs of extended + # properties to add to the storage account. The key is used as the + # property name and the value as its value. (optional) + # + # Returns None + # Fails with RuntimeError if invalid options specified + def update_storage_account(name, options = {}) + if get_storage_account name + Loggerx.info "Account '#{name}' exists, updating..." + body = Serialization.storage_update_to_xml options + request_path = "/services/storageservices/#{name}" + request = ManagementHttpRequest.new(:put, request_path, body) + request.call + else + Loggerx.warn "Storage Account '#{name}' does not exist. Skipped..." + end + end + + # Public: Deletes the specified storage account of given subscription id + # from Windows Azure. # # ==== Attributes # diff --git a/lib/azure/virtual_machine_management/virtual_machine_management_service.rb b/lib/azure/virtual_machine_management/virtual_machine_management_service.rb index f7c96e554..a1e8cef8d 100644 --- a/lib/azure/virtual_machine_management/virtual_machine_management_service.rb +++ b/lib/azure/virtual_machine_management/virtual_machine_management_service.rb @@ -71,7 +71,8 @@ module Azure # ==== Attributes # # * +params+ - Hash. parameters. - # * +Options+ - Hash. Optional parameters. + # * +options+ - Hash. Optional parameters. + # * +add_role+ - true/false. Optional Parameter. Default is false # # ==== Params # @@ -81,6 +82,8 @@ module Azure # * +:password+ - String. A description for the hosted service. # * +:image+ - String. Name of the disk image to use to create the virtual machine. # * +:location+ - String. The location where the virtual machine will be created. + # * +:affinity_group_name - String. The affinity group name to be used + # for the cloud service and the storage account if these do not exist. # # ==== Options # @@ -96,36 +99,60 @@ module Azure # * +:vm_size+ - String. Specifies the size of the virtual machine instance. # * +:winrm_transport+ - Array. Specifies WINRM transport protocol. # + # ==== add_role + # + # Accepted values are: + # * +false+ - Will add a new deployment in a cloud service. + # * +true+ - Will add a new role to a cloud service. Atleast one + # deployment should exist before you can add a role. + # # Returns Azure::VirtualMachineManagement::VirtualMachine objects of newly created instance. - def create_virtual_machine(params, options={}) + # + # See: + # http://msdn.microsoft.com/en-us/library/windowsazure/jj157194.aspx + # http://msdn.microsoft.com/en-us/library/windowsazure/jj157186.aspx + def create_virtual_machine(params, options = {}, add_role = false) options[:os_type] = get_os_type(params[:image]) validate_deployment_params(params, options) - options[:cloud_service_name] = generate_cloud_service_name(params[:vm_name]) unless options[:cloud_service_name] - options[:storage_account_name] = generate_storage_account_name(params[:vm_name]) unless options[:storage_account_name] - optionals = {} - if options[:virtual_network_name] - virtual_network_service = Azure::VirtualNetworkManagementService.new - virtual_networks = virtual_network_service.list_virtual_networks.select{|x| x.name == options[:virtual_network_name]} - if virtual_networks.empty? - Loggerx.error_with_exit "Virtual network #{options[:virtual_network_name]} doesn't exists" - else - optionals[:affinity_group_name] = virtual_networks.first.affinity_group - end - elsif options[:affinity_group_name] - optionals[:affinity_group_name] = options[:affinity_group_name] - else - optionals[:location] = params[:location] - end - cloud_service = Azure::CloudServiceManagementService.new - cloud_service.create_cloud_service(options[:cloud_service_name], optionals) - cloud_service.upload_certificate(options[:cloud_service_name],params[:certificate]) unless params[:certificate].empty? - Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], optionals) + options[:deployment_name] ||= options[:cloud_service_name] + + unless add_role + Loggerx.info "Creating deploymnent..." + options[:cloud_service_name] = generate_cloud_service_name(params[:vm_name]) unless options[:cloud_service_name] + options[:storage_account_name] = generate_storage_account_name(params[:vm_name]) unless options[:storage_account_name] + + optionals = {} + + if options[:virtual_network_name] + virtual_network_service = Azure::VirtualNetworkManagementService.new + virtual_networks = virtual_network_service.list_virtual_networks.select{|x| x.name == options[:virtual_network_name]} + if virtual_networks.empty? + Loggerx.error_with_exit "Virtual network #{options[:virtual_network_name]} doesn't exists" + else + optionals[:affinity_group_name] = virtual_networks.first.affinity_group + end + elsif options[:affinity_group_name] + optionals[:affinity_group_name] = options[:affinity_group_name] + else + optionals[:location] = params[:location] + end + + cloud_service = Azure::CloudServiceManagementService.new + cloud_service.create_cloud_service(options[:cloud_service_name], optionals) + cloud_service.upload_certificate(options[:cloud_service_name],params[:certificate]) unless params[:certificate].empty? + Azure::StorageManagementService.new.create_storage_account(options[:storage_account_name], optionals) + body = Serialization.deployment_to_xml(params,options) + path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments" + else + Loggerx.info "Deployment exists, adding role..." + body = Serialization.role_to_xml(params, options).to_xml + path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments/#{options[:deployment_name]}/roles" + end - body = Serialization.deployment_to_xml(params,options) - path = "/services/hostedservices/#{options[:cloud_service_name]}/deployments" Loggerx.info "Deployment in progress..." request = ManagementHttpRequest.new(:post, path, body) request.call + get_virtual_machine(params[:vm_name],options[:cloud_service_name]) rescue Exception => e e.message diff --git a/test/fixtures/create_storage_desc_error.xml b/test/fixtures/create_storage_desc_error.xml new file mode 100644 index 000000000..92ceafe8a --- /dev/null +++ b/test/fixtures/create_storage_desc_error.xml @@ -0,0 +1,5 @@ + + + BadRequest + The description is too long + \ No newline at end of file diff --git a/test/fixtures/create_storage_extendedprop_error.xml b/test/fixtures/create_storage_extendedprop_error.xml new file mode 100644 index 000000000..cfd89534f --- /dev/null +++ b/test/fixtures/create_storage_extendedprop_error.xml @@ -0,0 +1,8 @@ + + + BadRequest + verbose............................................... + ........................................................................................ + .........................This is a lengthy value............................................................... + ........................................................................................ value is too long. Only 255 characters are permitted + \ No newline at end of file diff --git a/test/fixtures/create_storage_extendedpropname_error.xml b/test/fixtures/create_storage_extendedpropname_error.xml new file mode 100644 index 000000000..1ee3af848 --- /dev/null +++ b/test/fixtures/create_storage_extendedpropname_error.xml @@ -0,0 +1,6 @@ + + + BadRequest + Extended property name blobnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +nnnnnnnnns is too long. Only 64 characters are permitted. + \ No newline at end of file diff --git a/test/fixtures/create_storage_full_error.xml b/test/fixtures/create_storage_full_error.xml new file mode 100644 index 000000000..fd7c45d14 --- /dev/null +++ b/test/fixtures/create_storage_full_error.xml @@ -0,0 +1,6 @@ + + + BadRequest + The subscription policy limit for resource type 'storage account count' was exceeded. The limit for resourc +e type 'storage account count' is 26 per subscription, the current count is 26, and the requested increment is 1. + \ No newline at end of file diff --git a/test/fixtures/create_storage_label_error.xml b/test/fixtures/create_storage_label_error.xml new file mode 100644 index 000000000..0d8874131 --- /dev/null +++ b/test/fixtures/create_storage_label_error.xml @@ -0,0 +1,5 @@ + + + BadRequest + The label is not a valid storage account label. + \ No newline at end of file diff --git a/test/fixtures/create_storage_location_error.xml b/test/fixtures/create_storage_location_error.xml new file mode 100644 index 000000000..e2925f7da --- /dev/null +++ b/test/fixtures/create_storage_location_error.xml @@ -0,0 +1,5 @@ + + + BadRequest + The location constraint is not valid + \ No newline at end of file diff --git a/test/fixtures/create_storage_name_error.xml b/test/fixtures/create_storage_name_error.xml new file mode 100644 index 000000000..5a7f34e87 --- /dev/null +++ b/test/fixtures/create_storage_name_error.xml @@ -0,0 +1,6 @@ + + + BadRequest + The name is not a valid storage account name. Storage account names must be between 3 and 24 characters in +length and use numbers and lower-case letters only. + \ No newline at end of file diff --git a/test/fixtures/delete_storage_container_error.xml b/test/fixtures/delete_storage_container_error.xml new file mode 100644 index 000000000..9fe6a0fde --- /dev/null +++ b/test/fixtures/delete_storage_container_error.xml @@ -0,0 +1,5 @@ + + + BadRequest + Storage account storagewithcontainer has container(s) which have an active image and/or disk artifacts. Ensure those artifacts are removed from the image repository before deleting this storage account. + \ No newline at end of file diff --git a/test/fixtures/delete_storage_error.xml b/test/fixtures/delete_storage_error.xml new file mode 100644 index 000000000..6b6626415 --- /dev/null +++ b/test/fixtures/delete_storage_error.xml @@ -0,0 +1,5 @@ + + + ResourceNotFound + The storage account invalidstorageacc was not found. + \ No newline at end of file diff --git a/test/fixtures/get_storage_account_error.xml b/test/fixtures/get_storage_account_error.xml new file mode 100644 index 000000000..0a1f3031c --- /dev/null +++ b/test/fixtures/get_storage_account_error.xml @@ -0,0 +1,5 @@ + + + Error + Resource not found + diff --git a/test/fixtures/get_storage_account_properties.xml b/test/fixtures/get_storage_account_properties.xml new file mode 100644 index 000000000..59a58c2b8 --- /dev/null +++ b/test/fixtures/get_storage_account_properties.xml @@ -0,0 +1,32 @@ + + + + https://management.core.windows.net/dummysubscriptionid/services/storageservices/storageacc + storageacc + + + agbabu1 + + Created + + http://storageacc.blob.core.windows.net/ + http://storageacc.queue.core.windows.net/ + http://storageacc.table.core.windows.net/ + + false + East US + + 2013-12-03T19:39:33Z + + + + + blob + verbose + + + + PersistentVMRole + + \ No newline at end of file diff --git a/test/fixtures/get_storage_account_properties_new.xml b/test/fixtures/get_storage_account_properties_new.xml new file mode 100644 index 000000000..a68c9a1e0 --- /dev/null +++ b/test/fixtures/get_storage_account_properties_new.xml @@ -0,0 +1,32 @@ + + + + https://management.core.windows.net/82f43e56-n564-4e01-hfie-4ce5362a101a/services/storageservices/storageacc + storageacc + + + agbabu1 + + Created + + http://storageacc.blob.core.windows.net/ + http://storageacc.queue.core.windows.net/ + http://storageacc.table.core.windows.net/ + + false + East US + + 2013-12-03T19:39:33Z + + + + + blob + verbose + + + + PersistentVMRole + + \ No newline at end of file diff --git a/test/fixtures/list_cloud_services.xml b/test/fixtures/list_cloud_services.xml index 1051311a9..3f8f77e0e 100644 --- a/test/fixtures/list_cloud_services.xml +++ b/test/fixtures/list_cloud_services.xml @@ -10,8 +10,18 @@ Created 2013-04-22T07:37:56Z 2013-04-22T07:38:09Z - + + + prop_1_name + prop_1_value + + + prop_2_name + prop_2_value + + + dummy-cert-value https://management.core.windows.net/268abcd2-fabc-4cd3-a4ea-80324bddff87/services/hostedservices/cloud-service-2 diff --git a/test/fixtures/list_sql_server_firewall_rdfe.xml b/test/fixtures/list_sql_server_firewall_rdfe.xml new file mode 100644 index 000000000..44c548f64 --- /dev/null +++ b/test/fixtures/list_sql_server_firewall_rdfe.xml @@ -0,0 +1,27 @@ + + + + AllowAllWindowsAzureIps + Microsoft.SqlAzure.FirewallRule + 0.0.0.0 + 0.0.0.0 + + + ClientIPAddress_2013-09-03_13:54:21 + Microsoft.SqlAzure.FirewallRule + 167.220.224.226 + 167.220.224.226 + + + Rule1 + Microsoft.SqlAzure.FirewallRule + 10.0.0.1 + 10.0.0.255 + + + Rule2 + Microsoft.SqlAzure.FirewallRule + 192.168.1.1 + 192.168.1.255 + + diff --git a/test/fixtures/update_storage_account.xml b/test/fixtures/update_storage_account.xml new file mode 100644 index 000000000..bf8736304 --- /dev/null +++ b/test/fixtures/update_storage_account.xml @@ -0,0 +1,16 @@ + + + + false + + + prop_1_name + prop_1_value + + + prop_2_name + prop_2_value + + + diff --git a/test/fixtures/update_storage_error.xml b/test/fixtures/update_storage_error.xml new file mode 100644 index 000000000..15f257624 --- /dev/null +++ b/test/fixtures/update_storage_error.xml @@ -0,0 +1,5 @@ + + + InvalidXmlRequest + The request body's XML was invalid or not correctly specified. + \ No newline at end of file diff --git a/test/fixtures/updated_storage_accounts.xml b/test/fixtures/updated_storage_accounts.xml new file mode 100644 index 000000000..8ca314ee8 --- /dev/null +++ b/test/fixtures/updated_storage_accounts.xml @@ -0,0 +1,53 @@ + + + + https://management.core.windows.net/268a3762-abcd-4cd3-a4ea-80e84bddff87/services/storageservices/storage1 + storage1 + + + West US + + Created + + http://storage1.blob.core.windows.net/ + http://storage1.queue.core.windows.net/ + http://storage1.table.core.windows.net/ + + true + West US + + East US + + + + + + https://management.core.windows.net/268a3762-abcd-4cd3-a4ea-80e84bddff87/services/storageservices/storage2 + storage2 + + West US + + Created + + http://storage2.blob.core.windows.net/ + http://storage2.queue.core.windows.net/ + http://storage2.table.core.windows.net/ + + false + West US + + East US + + + + + prop_1_name + prop_1_value + + + prop_2_name + prop_2_value + + + + \ No newline at end of file diff --git a/test/integration/database/create_sql_server_firewall_test.rb b/test/integration/database/create_sql_server_firewall_test.rb index 926a6438b..fc194931a 100644 --- a/test/integration/database/create_sql_server_firewall_test.rb +++ b/test/integration/database/create_sql_server_firewall_test.rb @@ -19,15 +19,15 @@ describe Azure::SqlDatabaseManagementService do subject { Azure::SqlDatabaseManagementService.new } let(:login_name) {'ms_open_tech'} let(:sql_server) { subject.create_server(login_name, 'User1@123', WindowsImageLocation) } + describe "#set_sql_server_firewall_rule" do before { Loggerx.expects(:puts).returns(nil).at_least(0) - Azure.config.management_endpoint = SqlServerEndpoint } after { - Azure.config.management_endpoint = ManagementServiceEndpoint + subject.delete_server(sql_server.name) } it "should adds a new server-level firewall rule for a SQL Database server with requester's IP address." do diff --git a/test/integration/database/create_sql_server_test.rb b/test/integration/database/create_sql_server_test.rb index adc58ccde..c56e92447 100644 --- a/test/integration/database/create_sql_server_test.rb +++ b/test/integration/database/create_sql_server_test.rb @@ -22,11 +22,6 @@ describe Azure::SqlDatabaseManagementService do before { Loggerx.expects(:puts).returns(nil).at_least(0) - Azure.config.management_endpoint = SqlServerEndpoint - } - - after { - Azure.config.management_endpoint = ManagementServiceEndpoint } it "should be able to create a new sql database server." do @@ -34,6 +29,8 @@ describe Azure::SqlDatabaseManagementService do sql_server.name.wont_be_nil sql_server.location.must_equal WindowsImageLocation sql_server.administrator_login.must_equal login_name + + subject.delete_server sql_server.name end it "errors if the sql server location does not exist" do diff --git a/test/integration/database/delete_sql_server_firewall_test.rb b/test/integration/database/delete_sql_server_firewall_test.rb index 1924ec1db..22bffeb69 100644 --- a/test/integration/database/delete_sql_server_firewall_test.rb +++ b/test/integration/database/delete_sql_server_firewall_test.rb @@ -22,14 +22,13 @@ describe Azure::SqlDatabaseManagementService do before { Loggerx.expects(:puts).returns(nil).at_least(0) - Azure.config.management_endpoint = SqlServerEndpoint subject.set_sql_server_firewall_rule(sql_server.name, "rule1") ip_range = {:start_ip_address => "10.20.30.0", :end_ip_address => "10.20.30.255"} subject.set_sql_server_firewall_rule(sql_server.name, "rule2", ip_range) } after { - Azure.config.management_endpoint = ManagementServiceEndpoint + subject.delete_server(sql_server.name) } describe "#delete_sql_server_firewall_rule" do @@ -58,7 +57,6 @@ describe Azure::SqlDatabaseManagementService do end it "errors if the sql server firewall rule does not exist" do - server_name = "unknown-server" rule_name = 'rule10' exception = assert_raises(RuntimeError) do subject.delete_sql_server_firewall_rule(sql_server.name, rule_name) diff --git a/test/integration/database/delete_sql_server_test.rb b/test/integration/database/delete_sql_server_test.rb index 36f076c09..d06fa8069 100644 --- a/test/integration/database/delete_sql_server_test.rb +++ b/test/integration/database/delete_sql_server_test.rb @@ -18,11 +18,6 @@ describe Azure::SqlDatabaseManagementService do before { Loggerx.expects(:puts).returns(nil).at_least(0) - Azure.config.management_endpoint = SqlServerEndpoint - } - - after { - Azure.config.management_endpoint = ManagementServiceEndpoint } subject { Azure::SqlDatabaseManagementService.new } diff --git a/test/integration/database/list_sql_server_firewall_test.rb b/test/integration/database/list_sql_server_firewall_test.rb index afdf50d3b..abf00dc61 100644 --- a/test/integration/database/list_sql_server_firewall_test.rb +++ b/test/integration/database/list_sql_server_firewall_test.rb @@ -23,14 +23,13 @@ describe Azure::SqlDatabaseManagementService do before { Loggerx.expects(:puts).returns(nil).at_least(0) - Azure.config.management_endpoint = SqlServerEndpoint ip_range = {:start_ip_address => "10.20.30.0", :end_ip_address => "10.20.30.255"} subject.set_sql_server_firewall_rule(sql_server.name, "rule1", ip_range) subject.set_sql_server_firewall_rule(sql_server.name, "rule2") } after { - Azure.config.management_endpoint = ManagementServiceEndpoint + subject.delete_server(sql_server.name) } it "returns a list of SQL databse server firewall" do diff --git a/test/integration/database/list_sql_servers_test.rb b/test/integration/database/list_sql_servers_test.rb index 50dd116a0..8a4a0ce9d 100644 --- a/test/integration/database/list_sql_servers_test.rb +++ b/test/integration/database/list_sql_servers_test.rb @@ -22,22 +22,21 @@ describe Azure::SqlDatabaseManagementService do before { Loggerx.expects(:puts).returns(nil).at_least(0) - Azure.config.management_endpoint = SqlServerEndpoint - subject.create_server(login_name, 'User1@123', WindowsImageLocation) - subject.create_server(login_name, 'User2@123', 'East US') - } - - after { - Azure.config.management_endpoint = ManagementServiceEndpoint } it "returns a list of SQL databse servers" do + server1 = subject.create_server(login_name, 'User1@123', WindowsImageLocation) + server2 = subject.create_server(login_name, 'User2@123', 'East US') + sql_servers = subject.list_servers sql_servers.wont_be_nil sql_servers.must_be_kind_of Array sql_server = sql_servers.first sql_server.must_be_kind_of Azure::SqlDatabaseManagement::SqlDatabase assert_operator sql_servers.size, :>=, 2 + + subject.delete_server(server1.name) + subject.delete_server(server2.name) end end diff --git a/test/integration/database/reset_password_sql_server_test.rb b/test/integration/database/reset_password_sql_server_test.rb index 6b267402a..1c967cda8 100644 --- a/test/integration/database/reset_password_sql_server_test.rb +++ b/test/integration/database/reset_password_sql_server_test.rb @@ -24,11 +24,10 @@ describe Azure::SqlDatabaseManagementService do before { Loggerx.expects(:puts).returns(nil).at_least(0) - Azure.config.management_endpoint = SqlServerEndpoint } after { - Azure.config.management_endpoint = ManagementServiceEndpoint + subject.delete_server(sql_server.name) } it "should be able to reset password of sql database server." do diff --git a/test/integration/storage_management/storage_management_create_test.rb b/test/integration/storage_management/storage_management_create_test.rb new file mode 100644 index 000000000..442ff6f98 --- /dev/null +++ b/test/integration/storage_management/storage_management_create_test.rb @@ -0,0 +1,289 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#-------------------------------------------------------------------------- +require "test_helper" +require 'azure/storage_management/storage_account' + +describe Azure::StorageManagementService do + + subject { Azure::StorageManagementService.new } + let(:request_path) {'/services/storageservices'} + let(:method) { :post } + let(:mock_request){ mock() } + let(:get_storage_account_properties) { Fixtures["get_storage_account_properties_new"] } + let(:responseStorageProps) { + responseStorageProps = mock() + responseStorageProps.stubs(:body).returns(get_storage_account_properties) + responseStorageProps + } + let(:responseStorageProps_body) {Nokogiri::XML responseStorageProps.body} + let(:create_storage_name_error) { Fixtures["create_storage_name_error"] } + let(:response) { + response = mock() + response.stubs(:body).returns(create_storage_name_error) + response + } + let(:response_body) {Nokogiri::XML response.body} + + let(:create_storage_location_error) { Fixtures["create_storage_location_error"] } + let(:responseLoc) { + responseLoc = mock() + responseLoc.stubs(:body).returns(create_storage_location_error) + responseLoc + } + let(:responseLoc_body) {Nokogiri::XML responseLoc.body} + let(:create_storage_desc_error) { Fixtures["create_storage_desc_error"] } + let(:responseDescErr) { + responseDescErr = mock() + responseDescErr.stubs(:body).returns(create_storage_desc_error) + responseDescErr + } + let(:responseDescErr_body) {Nokogiri::XML responseDescErr.body} + + let(:create_storage_extendedprop_error) { Fixtures["create_storage_extendedprop_error"] } + let(:responseExtndErr) { + responseExtndErr = mock() + responseExtndErr.stubs(:body).returns(create_storage_extendedprop_error) + responseExtndErr + } + let(:responseExtndErr_body) {Nokogiri::XML responseExtndErr.body} + let(:create_storage_extendedpropname_error) { Fixtures["create_storage_extendedpropname_error"] } + let(:responseExtndNameErr) { + responseExtndNameErr = mock() + responseExtndNameErr.stubs(:body).returns(create_storage_extendedpropname_error) + responseExtndNameErr + } + + let(:responseExtndNameErr_body) {Nokogiri::XML responseExtndNameErr.body} + + let(:create_storage_label_error) { Fixtures["create_storage_label_error"] } + let(:responseLabelErr) { + responseLabelErr = mock() + responseLabelErr.stubs(:body).returns(create_storage_label_error) + responseLabelErr + } + + let(:responseLabelErr_body) {Nokogiri::XML responseLabelErr.body} + let(:paramsLocInvalid){ + { + :location => "West US", + } + } + let(:paramsLoc){ + { + :location => "West US", + } + } + let(:paramsDesc){ + { + :description => "Description", + :location => "West US", + } + } + let(:paramsDescInvalid){ + { + :location => "West US", + :description => "..............................................................................................................................................................................................................................................................................................................", + } + } + let(:paramsAffinityGrp){ + { + :affinity_group_name => "agbabu1" + } + } + let(:paramslocGeoTrue){ + { + :location => "West US", + :geo_replication_enabled => 'true', + } + } + let(:paramslocGeoFalse){ + { + :location => "West US", + :geo_replication_enabled => 'false', + } + } + let(:paramsExtendedPropsValue){ + { + :location => "West US", + :extended_properties =>{:blobs =>'verbose............................................... + ........................................................................................ + .........................This is a lengthy value............................................................... + ........................................................................................ + ',:test =>'test'}, + } + } + let(:paramsExtendedProps){ + { + :location => "West US", + :extended_properties =>{:blobs =>'verbose',:test =>'test'}, + } + } + let(:paramsExtendedPropsName){ + { + :location => "West US", + :extended_properties =>{:blobnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnns =>'verbose',:test =>'test'}, + } + } + let(:paramsLabel){ + { + :location => "West US", + :label => "ValidLabel" + } + } + + let(:paramsLabelInvalid){ + { + :location => "West US", + :label => "ValidLabel................................................................................................................. + ............................................ + ................................... + ...............................", + } + } + before{ + Loggerx.expects(:puts).returns(nil).at_least(0) + } + +describe "#create_storage_account" do + + it "Create storage account with long name" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('longstoragename..............................................').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns response_body + subject.create_storage_account 'longstoragename..............................................' + msg = response_body.at('Error') + assert_match(/^The name is not a valid storage account name. Storage account names must be between 3 and 24 characters in\nlength and use numbers and lower-case letters only.*/, msg.at('Message').text) + end + + it "Create storage account name with spl characters" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('%^%$$^$^').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns response_body + subject.create_storage_account '%^%$$^$^' + msg = response_body.at('Error') + assert_match(/^The name is not a valid storage account name. Storage account names must be between 3 and 24 characters in +length and use numbers and lower-case letters only.*/, msg.at('Message').text) + end + + it "Create storage account with valid name and invalid location" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('invalidlocation').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns responseLoc + subject.create_storage_account('invalidlocation',paramsLocInvalid) + msg = responseLoc_body.at('Error') + assert_match(/^The location constraint is not valid*/, msg.at('Message').text) + end + + + it "Create valid storage account with only name and valid location" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns 'succeeded' + subject.create_storage_account('storageacc',paramsLoc) + Azure::StorageManagementService.any_instance.stubs(:get_storage_account_properties).with('storageacc').returns(responseStorageProps) + subject.get_storage_account_properties('storageacc') + msg = responseStorageProps_body.at('StorageServiceProperties') + assert_match(/^Created*/, msg.at('Status').text) + end + + it "Create valid storage account with only name and valid description" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validdesc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns responseLoc + subject.create_storage_account('validdesc',paramsDesc) + msg = responseLoc_body.at('Error') + assert_match(/^The location constraint is not valid*/, msg.at('Message').text) + end + + it "Create valid storage account with only name, location and long description" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('longdesc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns responseDescErr_body + subject.create_storage_account('longdesc',paramsDescInvalid) + msg = responseDescErr_body.at('Error') + assert_match(/^The description is too long*/, msg.at('Message').text) + end + + it "Create storage account with name and affinity group" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns "" + subject.create_storage_account('storageacc',paramsAffinityGrp) + Azure::StorageManagementService.any_instance.stubs(:get_storage_account_properties).with('storageacc').returns(responseStorageProps) + subject.get_storage_account_properties('storageacc') + msg = responseStorageProps_body.at('StorageServiceProperties') + assert_match(/^Created*/, msg.at('Status').text) + assert_match(/^agbabu1*/, msg.at('AffinityGroup').text) + end + + it "Create storage account with name, location and geo_replication_enabled true" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns "" + subject.create_storage_account('storageacc',paramslocGeoTrue) + Azure::StorageManagementService.any_instance.stubs(:get_storage_account_properties).with('storageacc').returns(responseStorageProps) + subject.get_storage_account_properties('storageacc') + msg = responseStorageProps_body.at('StorageServiceProperties') + assert_match(/^Created*/, msg.at('Status').text) + end + + it "Create storage account with name, location and geo_replication_enabled false" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns "" + subject.create_storage_account('storageacc',paramslocGeoFalse) + Azure::StorageManagementService.any_instance.stubs(:get_storage_account_properties).with('storageacc').returns(responseStorageProps) + subject.get_storage_account_properties('storageacc') + msg = responseStorageProps_body.at('StorageServiceProperties') + assert_match(/^Created*/, msg.at('Status').text) + assert_match(/^false*/, msg.at('GeoReplicationEnabled').text) + end + + it "Create storage account with name, location and extended properties" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns "succeeded" + msg = subject.create_storage_account('storageacc',paramsExtendedProps) + assert_match(/^succeeded*/, msg) + end + + it "Create storage account with name, location and lengthy extended property value" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns responseExtndErr + subject.create_storage_account('storageacc',paramsExtendedPropsValue) + msg = responseExtndErr_body.at('Error') + assert_match(/^verbose............................................... + ........................................................................................ + .........................This is a lengthy value............................................................... + ........................................................................................ value is too long. Only 255 characters are permitted*/, msg.at('Message').text) + end + + it "Create storage account with name, location and lengthy extended property name" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns responseExtndNameErr + subject.create_storage_account('storageacc',paramsExtendedPropsName) + msg = responseExtndNameErr_body.at('Error') + assert_match(/^Extended property name blobnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn +nnnnnnnnns is too long. Only 64 characters are permitted.*/, msg.at('Message').text) + end + + it "Create storage account with name, location and label" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns "" + subject.create_storage_account('storageacc',paramsLabel) + msg = responseExtndNameErr_body.at('Error') + assert_match(/^*/, "") + end + + it "Create storage account with name, location and lengthy label" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('storageacc').returns(false) + ManagementHttpRequest.any_instance.expects(:call).returns responseLabelErr + subject.create_storage_account('storageacc',paramsLabelInvalid) + msg = responseLabelErr_body.at('Error') + assert_match(/^The label is not a valid storage account label.*/, msg.at('Message').text) + end + + end +end \ No newline at end of file diff --git a/test/integration/storage_management/storage_management_delete_test.rb b/test/integration/storage_management/storage_management_delete_test.rb new file mode 100644 index 000000000..b7b3dff09 --- /dev/null +++ b/test/integration/storage_management/storage_management_delete_test.rb @@ -0,0 +1,59 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#-------------------------------------------------------------------------- +require "test_helper" +require 'azure/storage_management/storage_account' + +describe Azure::StorageManagementService do + + subject { Azure::StorageManagementService.new } + let(:request_path) {'/services/storageservices'} + let(:method) { :post } + let(:mock_request){ mock() } + let(:delete_storage_error) { Fixtures["delete_storage_error"] } + let(:responseResourceNotFound) { + responseResourceNotFound = mock() + responseResourceNotFound.stubs(:body).returns(delete_storage_error) + responseResourceNotFound + } + let(:responseResourceNotFound_body) {Nokogiri::XML responseResourceNotFound.body} + let(:delete_storage_container_error) { Fixtures["delete_storage_container_error"] } + let(:responseBadRequest) { + responseBadRequest = mock() + responseBadRequest.stubs(:body).returns(delete_storage_container_error) + responseBadRequest + } + let(:responseBadRequest_body) {Nokogiri::XML responseBadRequest.body} + + before{ + Loggerx.expects(:puts).returns(nil).at_least(0) + } + +describe "#delete_storage_account" do + + it "Delete non existing storage account" do + ManagementHttpRequest.any_instance.expects(:call).returns responseResourceNotFound_body + subject.delete_storage_account 'invalidstorageacc' + msg = responseResourceNotFound_body.at('Error') + assert_match(/^ResourceNotFound*/, msg.at('Code').text) + end + + it "Delete storage account with containers having disks" do + ManagementHttpRequest.any_instance.expects(:call).returns responseBadRequest_body + subject.delete_storage_account 'storagewithcontainer' + msg = responseBadRequest_body.at('Error') + assert_match(/^BadRequest*/, msg.at('Code').text) + end + end +end \ No newline at end of file diff --git a/test/integration/storage_management/storage_management_get_properties_test.rb b/test/integration/storage_management/storage_management_get_properties_test.rb new file mode 100644 index 000000000..5042de7ef --- /dev/null +++ b/test/integration/storage_management/storage_management_get_properties_test.rb @@ -0,0 +1,55 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#-------------------------------------------------------------------------- +require "test_helper" +require 'azure/storage_management/storage_account' + +describe Azure::StorageManagementService do + + subject { Azure::StorageManagementService.new } + let(:request_path) {'/services/storageservices'} + let(:method) { :get } + let(:mock_request){ mock() } + let(:get_storage_account_properties) { Fixtures["get_storage_account_properties_new"] } + let(:responseStorageProps) { + responseStorageProps = mock() + responseStorageProps.stubs(:body).returns(get_storage_account_properties) + responseStorageProps + } + let(:responseStorageProps_body) {Nokogiri::XML responseStorageProps.body} + + before{ + Loggerx.expects(:puts).returns(nil).at_least(0) + } + +describe "#create_storage_account" do + + it "Get storage account properties" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account_properties).with('storageacc').returns(responseStorageProps) + subject.get_storage_account_properties('storageacc') + msg = responseStorageProps_body.at('StorageServiceProperties') + + #Description assertion fails as there is a bug in RDFE for the description value not getting updated accordingly + assert_match(/^Description*/, msg.at('Description').text) + assert_match(/^agbabu1*/, msg.at('AffinityGroup').text) + assert_match(/^VmFsaWRMYWJlbA==*/, msg.at('Label').text) + assert_match(/^Created*/, msg.at('Created').text) + assert_match(/^false*/, msg.at('GeoReplicationEnabled').text) + assert_match(/^false*/, msg.at('GeoPrimaryRegion').text) + msg = responseStorageProps_body.at('ExtendedProperty') + assert_match(/^verbose*/, msg.at('blob').text) + end + + end +end \ No newline at end of file diff --git a/test/integration/storage_management/storage_management_get_test.rb b/test/integration/storage_management/storage_management_get_test.rb new file mode 100644 index 000000000..c23ee836d --- /dev/null +++ b/test/integration/storage_management/storage_management_get_test.rb @@ -0,0 +1,67 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#-------------------------------------------------------------------------- +require "test_helper" + +describe Azure::StorageManagementService do + + subject { Azure::StorageManagementService.new } + let(:request_path) {'/services/storageservices'} + let(:get_storage_accounts_xml) { Fixtures["get_storage_properties"] } + let(:method) { :get } + let(:mock_request){ mock() } + let(:response) { + response = mock() + response.stubs(:body).returns(get_storage_accounts_xml) + response + } + let(:get_storage_account_error_xml) { Fixtures["get_storage_account_error"] } + let(:response1) { + response1 = mock() + response1.stubs(:body).returns(get_storage_account_error_xml) + response1 + } + let(:response_body) {Nokogiri::XML response.body} + before{ + Loggerx.expects(:puts).returns(nil).at_least(0) + } + +describe "#get_storage_account" do + it "Get storage account by specifying null" do + result = subject.get_storage_account nil + result.must_equal false + end + + it "Get storage account with valid name" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validname').returns(true) + result = subject.get_storage_account 'validname' + result.must_equal true + end + + it "Get storage account with invalid name - spl characters" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('invalidname@@@@@@@@@@@@@@@@@@').returns(false) + result = subject.get_storage_account 'invalidname@@@@@@@@@@@@@@@@@@' + result.must_equal false + end + + it "Get storage account with invalid name - long name" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('invalidname....................................').returns(false) + result = subject.get_storage_account 'invalidname....................................' + result.must_equal false + end +end +end + + + \ No newline at end of file diff --git a/test/integration/storage_management/storage_management_update_test.rb b/test/integration/storage_management/storage_management_update_test.rb new file mode 100644 index 000000000..6fe7d03f7 --- /dev/null +++ b/test/integration/storage_management/storage_management_update_test.rb @@ -0,0 +1,136 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#-------------------------------------------------------------------------- +require "test_helper" +require 'azure/storage_management/storage_account' + +describe Azure::StorageManagementService do + + subject { Azure::StorageManagementService.new } + let(:request_path) {'/services/storageservices'} + let(:method) { :post } + let(:mock_request){ mock() } + let(:update_storage_error) { Fixtures["update_storage_error"] } + let(:response) { + response = mock() + response.stubs(:body).returns(update_storage_error) + response + } + let(:response_body) {Nokogiri::XML response.body} + let(:params){ + { + :label => "SampleLabel", + :description => "SampleDescription", + + } + } + let(:params1){ + { + :label => "SampleLabel", + :geo_replication_enabled => 'invalid', + } + } + let(:params2){ + { + :label => "SampleLabel", + :description => ".............................................. + ............................................................... + ................................................................ + ................................................................ + ..................................................................", + :geo_replication_enabled => 'invalid', + } + } + let(:params3){ + { + :label => ".............................................. + ............................................................... + ................................................................ + ................................................................ + ..................................................................", + :description => "sampledescription", + :geo_replication_enabled => 'invalid', + } + } + let(:params4){ + { + :label => "@#@#@#@#@", + :description => nil, + :geo_replication_enabled => nil, + } + } + + before{ + Loggerx.expects(:puts).returns(nil).at_least(0) + } + +describe "update_storage_account" do + + it "Update non existing storage account" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('nonexistantstorageacc').returns(false) + msg = subject.update_storage_account('nonexistantstorageacc',params) + assert_match(/^Storage Account 'nonexistantstorageacc' does not exist. Skipped...*/, msg) + end + + + it "Update invalid geo_replication_enabled value" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validstoragename').returns(true) + ManagementHttpRequest.any_instance.expects(:call).returns response_body + subject.update_storage_account('validstoragename',params1) + msg = response_body.at('Error') + assert_match(/^InvalidXmlRequest*/, msg.at('Code').text) + end + + + it "Update long description" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validstoragename').returns(true) + ManagementHttpRequest.any_instance.expects(:call).returns response_body + subject.update_storage_account('validstoragename',params2) + msg = response_body.at('Error') + assert_match(/^InvalidXmlRequest*/, msg.at('Code').text) + end + + it "Update long label" do + storagename='nonexistantstorageacc' + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validstoragename').returns(true) + ManagementHttpRequest.any_instance.expects(:call).returns response_body + subject.update_storage_account('validstoragename',params3) + msg = response_body.at('Error') + assert_match(/^InvalidXmlRequest*/, msg.at('Code').text) + end + + it "Update long description" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validstoragename').returns(true) + ManagementHttpRequest.any_instance.expects(:call).returns response_body + subject.update_storage_account('validstoragename',params2) + msg = response_body.at('Error') + assert_match(/^InvalidXmlRequest*/, msg.at('Code').text) + end + + it "Update label with spl characters" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validstoragename').returns(true) + ManagementHttpRequest.any_instance.expects(:call).returns response_body + subject.update_storage_account('validstoragename',params3) + msg = response_body.at('Error') + assert_match(/^InvalidXmlRequest*/, msg.at('Code').text) + end + + it "Update valid geo_location_enabled" do + Azure::StorageManagementService.any_instance.stubs(:get_storage_account).with('validstoragename').returns(true) + ManagementHttpRequest.any_instance.expects(:call).returns "" + subject.update_storage_account('validstoragename',params3) + assert_match(/^*/, "") + end + end +end \ No newline at end of file diff --git a/test/integration/test_helper.rb b/test/integration/test_helper.rb index 838a3788f..9876f20c9 100644 --- a/test/integration/test_helper.rb +++ b/test/integration/test_helper.rb @@ -31,8 +31,6 @@ Azure::Core.configure do |config| end StorageAccountName = random_string('storagetest',10) -SqlServerEndpoint = "https://management.database.windows.net:8443/" -ManagementServiceEndpoint = Azure.config.management_endpoint Images = Azure::VirtualMachineImageManagementService.new.list_virtual_machine_images LinuxImage = Images.select{|image| image.os_type == 'Linux'}.first WindowsImage = Images.select{|image| image.os_type == 'Windows'}.first diff --git a/test/integration/vm/VM_Create_test.rb b/test/integration/vm/VM_Create_test.rb index b54fe4646..fcb7f6575 100644 --- a/test/integration/vm/VM_Create_test.rb +++ b/test/integration/vm/VM_Create_test.rb @@ -25,6 +25,7 @@ describe Azure::VirtualMachineManagementService do let(:password) { 'Admin123' } let(:certificate) { Fixtures["certificate.pem"] } let(:private_key) { Fixtures["privatekey.key"] } + let(:params){ { :vm_name => virtual_machine_name, @@ -50,8 +51,9 @@ describe Azure::VirtualMachineManagementService do let(:options){ { :storage_account_name => storage_account_name, - :virtual_network_name => in_vnet_name, - :subnet_name => 'Subnet-1' + :cloud_service_name => cloud_service_name, + + :vm_size =>'A7' } } @@ -82,7 +84,7 @@ describe Azure::VirtualMachineManagementService do describe "#deployment" do it "should set options hash with valid cloud_service_name, deployment_name, storage_account_name and virtual network" do - subject.create_virtual_machine(params, options) + subject.create_virtual_machine(params, options,add_role=false) cloud_name = options[:cloud_service_name] virtual_machine = subject.get_virtual_machine(virtual_machine_name, cloud_name) virtual_machine.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine @@ -93,13 +95,11 @@ describe Azure::VirtualMachineManagementService do virtual_machine.os_type.must_equal 'Linux' options[:storage_account_name].wont_be_nil assert_match(/^#{params[:vm_name]+'-service'}*/, cloud_name) - virtual_machine.virtual_network_name.must_equal in_vnet_name - virtual_machine.virtual_network.must_be_kind_of Azure::VirtualNetworkManagement::VirtualNetwork end it "should creates http and https enabled winrm virtual machine without certificate." do default_options.merge!(:winrm_transport => ['https','http']) - subject.create_virtual_machine(windows_params, default_options) + subject.create_virtual_machine(windows_params, default_options,add_role=false) result = subject.get_virtual_machine(virtual_machine_name, cloud_service_name) result.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine assert_equal(result.os_type, 'Windows',"Error in the OS type of VI created") @@ -114,7 +114,7 @@ describe Azure::VirtualMachineManagementService do it "should creates https enabled winrm virtual machine using certificate." do default_options.merge!(:winrm_transport => ['https'], :private_key_file => private_key, :certificate_file => certificate) - subject.create_virtual_machine(windows_params, default_options) + subject.create_virtual_machine(windows_params, default_options,add_role=false) result = subject.get_virtual_machine(virtual_machine_name, cloud_service_name) result.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine assert_equal(result.os_type, 'Windows',"Error in the OS type of VI created") @@ -127,7 +127,7 @@ describe Azure::VirtualMachineManagementService do it "should creates windows virtual machine without winrm." do default_options.merge!(:winrm_transport => ['none']) - subject.create_virtual_machine(windows_params, default_options) + subject.create_virtual_machine(windows_params, default_options,add_role=false) result = subject.get_virtual_machine(virtual_machine_name, cloud_service_name) result.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine assert_equal(result.os_type, 'Windows',"Error in the OS type of VI created") @@ -141,46 +141,27 @@ describe Azure::VirtualMachineManagementService do it "created linux virtual machine should be accessible using password and certificate" do default_options.merge!(:private_key_file => private_key, :certificate_file => certificate) - subject.create_virtual_machine(params, default_options) + subject.create_virtual_machine(params, default_options,add_role=false) result = subject.get_virtual_machine(virtual_machine_name, cloud_service_name) result.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine assert_equal(result.os_type, 'Linux',"Error in the OS type of VI created") end - it "ssh should be accessible using certificate on different port and virtual machine size should be Medium" do - params.delete(:password) - default_options.merge!(:ssh_port => '2222', :vm_size => 'Medium', :private_key_file => private_key, :certificate_file => certificate) - subject.create_virtual_machine(params, default_options) - result = subject.get_virtual_machine(virtual_machine_name, cloud_service_name) - result.must_be_kind_of Azure::VirtualMachineManagement::VirtualMachine - endpoints = {} - result.tcp_endpoints.each do |tcp_endpoint| - endpoints[tcp_endpoint["Name"]] = - { - "PublicPort" => tcp_endpoint["PublicPort"], - "LocalPort" => tcp_endpoint["LocalPort"] - } - end - endpoints.keys.must_include "SSH" - assert_equal(endpoints["SSH"]["PublicPort"], "2222") - assert_equal(endpoints["SSH"]["LocalPort"], "22") - assert_equal(result.role_size, 'Medium') - end it "throws Runtime error as port value is beyond or less than actual range" do default_options.merge!(:tcp_endpoints => '80,166535:166535') - msg = subject.create_virtual_machine(params, default_options) + msg = subject.create_virtual_machine(params, default_options,add_role=false) assert_match(/invalid. Allowed values are 'a number between 1 to 65535'./i, msg) default_options.merge!(:tcp_endpoints => '80,0:0') - msg = subject.create_virtual_machine(params, default_options) + msg = subject.create_virtual_machine(params, default_options,add_role=false) assert_match(/invalid. Allowed values are 'a number between 1 to 65535'./i, msg) cloud_service.delete_cloud_service(cloud_service_name) end it "throws error when multiple VMs created under same DNS" do - subject.create_virtual_machine(params, default_options) + subject.create_virtual_machine(params, default_options,add_role=false) msg = subject.create_virtual_machine(windows_params, default_options) assert_match(/The specified deployment slot Production is occupied./i,msg) end @@ -199,34 +180,34 @@ describe Azure::VirtualMachineManagementService do it "error thrown when invalid storage account name is given" do default_options.merge!(:storage_account_name=>'storageuse_91') - msg = subject.create_virtual_machine(params, default_options) + msg = subject.create_virtual_machine(params, default_options,add_role=false) assert_match(/The name is not a valid storage account name./i, msg) cloud_service.delete_cloud_service(cloud_service_name) end it "error thrown when invalid cloud name is given" do default_options.merge!(:cloud_service_name => 'cloud-server-test_91') - msg = subject.create_virtual_machine(params, default_options) + msg = subject.create_virtual_machine(params, default_options,add_role=false) assert_match(/The hosted service name is invalid/i, msg) end it "error thrown when invalid deployment name provided" do default_options.merge!(:deployment_name => 'instance_B') - msg = subject.create_virtual_machine(params, default_options) + msg = subject.create_virtual_machine(params, default_options,add_role=false) assert_match(/The deployment name is invalid/i, msg) cloud_service.delete_cloud_service(cloud_service_name) end it "error thrown when invalid Virtual Machine name for Windows OS provided" do windows_params.merge!(:vm_name => "MSServerInstnce01") - msg = subject.create_virtual_machine(windows_params, default_options) + msg = subject.create_virtual_machine(windows_params, default_options,add_role=false) assert_match(/The computer name cannot be more than 15 characters long, be entirely numeric, or contain the following characters/i, msg) cloud_service.delete_cloud_service(cloud_service_name) end it "error thrown when blank password for Windows OS provided" do windows_params.delete(:password) - msg = subject.create_virtual_machine(windows_params, default_options) + msg = subject.create_virtual_machine(windows_params, default_options,add_role=false) assert_match(/You did not provide a valid 'password' value./i, msg) end diff --git a/test/support/virtual_machine_name_generator.rb b/test/support/virtual_machine_name_generator.rb index d86e1991c..a9276e688 100644 --- a/test/support/virtual_machine_name_generator.rb +++ b/test/support/virtual_machine_name_generator.rb @@ -72,17 +72,6 @@ class VirtualMachineNameGenerator end end - # Delete SQL servers - Azure.config.management_endpoint = SqlServerEndpoint - sql_database_service = Azure::SqlDatabaseManagementService.new - sql_database_servers = sql_database_service.list_servers - sql_database_servers.each do |sql_server| - if sql_server.administrator_login == 'ms_open_tech' - sql_database_service.delete_server(sql_server.name) rescue nil - end - end - Azure.config.management_endpoint = ManagementServiceEndpoint - # Delete disks disk_management_service = Azure::VirtualMachineImageManagement::VirtualMachineDiskManagementService.new disks = disk_management_service.list_virtual_machine_disks diff --git a/test/unit/cloud_service_management/serialization_test.rb b/test/unit/cloud_service_management/serialization_test.rb index 4cf987630..097ffc79a 100644 --- a/test/unit/cloud_service_management/serialization_test.rb +++ b/test/unit/cloud_service_management/serialization_test.rb @@ -20,19 +20,42 @@ describe Azure::CloudServiceManagement::Serialization do let(:cloud_services_from_xml) { Fixtures['list_cloud_services'] } describe '#cloud_services_from_xml' do - + let (:cloud_services_xml) { Nokogiri::XML(cloud_services_from_xml) } it 'accepts an XML string' do subject.cloud_services_from_xml Nokogiri::XML(cloud_services_from_xml) end it 'returns an Array of CloudService instances' do - results = subject.cloud_services_from_xml( - Nokogiri::XML(cloud_services_from_xml) - ) + results = subject.cloud_services_from_xml(cloud_services_xml) results.must_be_kind_of Array results[0].must_be_kind_of(Azure::CloudServiceManagement::CloudService) results.count.must_equal 2 end + + it 'populates all properties for a cloud service' do + results = subject.cloud_services_from_xml(cloud_services_xml) + cloud = results.first + cloud_xml = cloud_services_xml.css('HostedServices HostedService').first + cloud.url.must_equal(cloud_xml.css('Url').text) + cloud.name.must_equal(cloud_xml.css('ServiceName').text) + cloud.description.must_equal(cloud_xml.css('Description').text) + cloud.location.must_equal(cloud_xml.css('Location').text) + cloud.label.must_equal(Base64.decode64(cloud_xml.css('Label').text)) + cloud.status.must_equal(cloud_xml.css('Status').text) + cloud.date_created.must_equal(cloud_xml.css('DateCreated').text) + cloud.date_modified.must_equal(cloud_xml.css('DateLastModified').text) + cloud.extended_properties.size.must_equal( + cloud_xml.css('ExtendedProperties ExtendedProperty').size + ) + cloud_xml.css('ExtendedProperties ExtendedProperty').each do |p| + cloud.extended_properties[p.css('Name').text].must_equal( + p.css('Value').text + ) + end + cloud.default_winrm_certificate_thumbprint.must_equal( + cloud_xml.css('DefaultWinRMCertificateThumbprint').text + ) + end end describe '#cloud_services_to_xml' do @@ -42,6 +65,31 @@ describe Azure::CloudServiceManagement::Serialization do subject.cloud_services_to_xml cloud_service_name end + it 'uses name for label if label not provided' do + results = subject.cloud_services_to_xml( + cloud_service_name, + { + location: 'East Asia' + } + ) + + doc = Nokogiri::XML(results) + Base64.decode64(doc.css('Label').text).must_equal(cloud_service_name) + end + + it 'uses label when label is provided' do + results = subject.cloud_services_to_xml( + cloud_service_name, + { + location: 'East Asia', + label: 'My Label' + } + ) + + doc = Nokogiri::XML(results) + Base64.decode64(doc.css('Label').text).must_equal('My Label') + end + it 'serializes the argument to xml' do results = subject.cloud_services_to_xml( cloud_service_name, { location: 'West US' } @@ -52,15 +100,60 @@ describe Azure::CloudServiceManagement::Serialization do doc.css('Location').text.must_equal 'West US' end - it 'uses affinity_group if provided' do + it 'uses affinity_group if provided and ignores location' do results = subject.cloud_services_to_xml( cloud_service_name, - { affinity_group_name: 'my-affinity-group', location: 'West US' } + { + affinity_group_name: 'my-affinity-group', + location: 'West US' + } ) results.must_be_kind_of String doc = Nokogiri::XML results - doc.css('ServiceName').text.must_equal cloud_service_name - doc.css('AffinityGroup').text.must_equal 'my-affinity-group' + doc.css('ServiceName').text.must_equal(cloud_service_name) + doc.css('AffinityGroup').wont_be_empty + doc.css('AffinityGroup').text.must_equal('my-affinity-group') + doc.css('Location').must_be_empty + end + + it 'uses location when affinity group not provided' do + results = subject.cloud_services_to_xml( + cloud_service_name, + { + location: 'East Asia' + } + ) + + doc = Nokogiri::XML(results) + doc.css('AffinityGroup').must_be_empty + doc.css('Location').wont_be_empty + doc.css('Location').text.must_equal('East Asia') + end + + it 'serializes extended properties when provided' do + xtended_props = { + prop_1_name: 'prop_1_value', + prop_2_name: 'prop_2_value' + } + results = subject.cloud_services_to_xml( + cloud_service_name, + { + extended_properties: xtended_props + } + ) + + doc = Nokogiri::XML(results) + props_xml = doc.css('ExtendedProperties ExtendedProperty') + props_xml.wont_be_empty + props_xml.size.must_equal(2) + props_xml.css('Name').first.text.must_equal('prop_1_name') + props_xml.css('Value').first.text.must_equal( + xtended_props[:"#{props_xml.css('Name').first.text}"] + ) + props_xml.css('Name').last.text.must_equal('prop_2_name') + props_xml.css('Value').last.text.must_equal( + xtended_props[:"#{props_xml.css('Name').last.text}"] + ) end it 'serializes the options hash to xml' do @@ -73,4 +166,4 @@ describe Azure::CloudServiceManagement::Serialization do results.must_be_kind_of String end end -end \ No newline at end of file +end diff --git a/test/unit/database/serialization_test.rb b/test/unit/database/serialization_test.rb index a7e97c10e..3974dd86c 100644 --- a/test/unit/database/serialization_test.rb +++ b/test/unit/database/serialization_test.rb @@ -19,6 +19,15 @@ describe Azure::SqlDatabaseManagement::Serialization do let(:sql_servers_xml) { Fixtures['list_sql_database'] } + before { + @rdfe = Azure.config.disable_sql_rdfe + Azure.config.disable_sql_rdfe = 'true' + } + + after { + Azure.config.disable_sql_rdfe = "#{@rdfe}" + } + describe '#databases_from_xml' do it 'accepts an XML string' do diff --git a/test/unit/database/sql_database_server_service_test.rb b/test/unit/database/sql_database_server_service_test.rb index bd5191d51..1b25101f4 100644 --- a/test/unit/database/sql_database_server_service_test.rb +++ b/test/unit/database/sql_database_server_service_test.rb @@ -14,17 +14,24 @@ #-------------------------------------------------------------------------- require 'test_helper' -describe Azure::SqlDatabaseManagementService do +describe 'Azure::SqlDatabaseManagementService - non-RDFE Endpoint' do subject { Azure::SqlDatabaseManagementService.new } let(:response_headers) { {} } let(:mock_request) { mock } let(:response) { mock } let(:response_xml) { nil } + before do Loggerx.expects(:puts).returns(nil).at_least(0) mock_request.stubs(:headers).returns(response_headers) mock_request.expects(:call).returns(Nokogiri::XML response_xml).at_least(0) + @rdfe_off = Azure.config.disable_sql_rdfe + Azure.config.disable_sql_rdfe = 'true' + end + + after do + Azure.config.disable_sql_rdfe = "#{@rdfe_off}" end describe '#list_servers' do @@ -33,7 +40,7 @@ describe Azure::SqlDatabaseManagementService do let(:request_path) { '/servers' } before do - ManagementHttpRequest.stubs(:new).with( + SqlManagementHttpRequest.stubs(:new).with( method, request_path, nil @@ -91,7 +98,7 @@ describe Azure::SqlDatabaseManagementService do :list_servers ).returns([sql_server]) - ManagementHttpRequest.stubs(:new).with( + SqlManagementHttpRequest.stubs(:new).with( method, request_path, nil @@ -119,7 +126,7 @@ describe Azure::SqlDatabaseManagementService do let(:location) { 'West US' } before do - ManagementHttpRequest.stubs(:new).with( + SqlManagementHttpRequest.stubs(:new).with( method, request_path, anything diff --git a/test/unit/database/sql_database_service_rdfe_test.rb b/test/unit/database/sql_database_service_rdfe_test.rb new file mode 100644 index 000000000..b377e9e7b --- /dev/null +++ b/test/unit/database/sql_database_service_rdfe_test.rb @@ -0,0 +1,159 @@ +#------------------------------------------------------------------------- +# Copyright 2013 Microsoft Open Technologies, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +#-------------------------------------------------------------------------- +require 'test_helper' + +describe 'Azure::SqlDatabaseManagementService - RDFE Endpoint' do + subject { Azure::SqlDatabaseManagementService.new } + + let(:response_headers) { {} } + let(:mock_request) { mock } + let(:response) { mock } + let(:response_xml) { nil } + + before do + Loggerx.expects(:puts).returns(nil).at_least(0) + mock_request.stubs(:headers).returns(response_headers) + mock_request.expects(:call).returns(Nokogiri::XML response_xml).at_least(0) + @rdfe_off = Azure.config.disable_sql_rdfe + Azure.config.disable_sql_rdfe = nil + end + + after do + Azure.config.disable_sql_rdfe = "#{@rdfe_off}" + end + + describe '#list_servers' do + let(:response_xml) { Fixtures['list_sql_database'] } + let(:method) { :get } + let(:request_path) { '/services/sqlservers/servers' } + + before do + ManagementHttpRequest.stubs(:new).with( + method, + request_path, + nil + ).returns(mock_request) + end + + it 'assembles a URI for the request' do + subject.list_servers + end + + it 'returns a list of sql servers for the account' do + results = subject.list_servers + results.must_be_kind_of Array + results.length.must_equal 3 + sql_server = results.first + sql_server.must_be_kind_of Azure::SqlDatabaseManagement::SqlDatabase + sql_server.name.must_equal 'nn1koc2ney' + sql_server.administrator_login.must_equal 'SqlServer2' + sql_server.location.must_equal 'West US' + sql_server.feature_name.must_equal 'Premium Mode' + sql_server.feature_value.must_equal 'false' + end + end + + describe '#delete_server' do + before do + Azure::SqlDatabaseManagementService.any_instance.stubs( + :list_servers + ).returns([]) + end + + it 'error if sql server does not exists' do + s_name = 'unknown-server' + exception = assert_raises(Azure::Error::Error) do + subject.delete_server s_name + end + s_id = Azure.config.subscription_id + assert_match(/Subscription #{s_id} does not have server #{s_name}./i, + exception.message) + end + + end + + describe '#list_sql_server_firewall_rules' do + let(:response_xml) { Fixtures['list_sql_server_firewall_rdfe'] } + let(:method) { :get } + let(:sql_server_name) { 'server1' } + let(:request_path) { "/services/sqlservers/servers/#{sql_server_name}/firewallrules" } + + before do + sql_server = Azure::SqlDatabaseManagement::SqlDatabase.new do |server| + server.name = sql_server_name + end + Azure::SqlDatabaseManagementService.any_instance.stubs( + :list_servers + ).returns([sql_server]) + + ManagementHttpRequest.stubs(:new).with( + method, + request_path, + nil + ).returns(mock_request) + end + + it 'assembles a URI for the sql server firewall request' do + subject.list_sql_server_firewall_rules sql_server_name + end + + it 'returns a list of firewall of given sql servers' do + results = subject.list_sql_server_firewall_rules sql_server_name + results.must_be_kind_of Array + results.length.must_equal 4 + results.first.must_be_kind_of Hash + end + end + + describe '#create_server' do + let(:response_xml) { Fixtures['create_sql_database_server'] } + let(:method) { :post } + let(:request_path) { '/services/sqlservers/servers' } + let(:password) { 'User@123' } + let(:login) { 'ms_open_tech' } + let(:location) { 'West US' } + + before do + ManagementHttpRequest.stubs(:new).with( + method, + request_path, + anything + ).returns(mock_request) + + mock_request.stubs(:headers).returns(response_headers) + mock_request.expects(:call).returns( + Nokogiri::XML response_xml + ).at_least(0) + end + + it 'create sql server' do + sql_server = subject.create_server(login, password, location) + sql_server.name.must_equal 'gxyfzrhx2c' + sql_server.administrator_login.must_equal login + sql_server.location.must_equal location + end + end + + describe '#set_sql_server_firewall_rule' do + it 'create sql server' do + ip_range = { start_ip_address: '0.0.0.1', end_ip_address: '0.0.0.5' } + exception = assert_raises(RuntimeError) do + subject.set_sql_server_firewall_rule('zv2nfoah2t1', ip_range) + end + assert_match(/Missing parameter server_name or rule_name/i, + exception.message) + end + end +end diff --git a/test/unit/storage_management/serialization_test.rb b/test/unit/storage_management/serialization_test.rb index eae754786..56320c6a9 100644 --- a/test/unit/storage_management/serialization_test.rb +++ b/test/unit/storage_management/serialization_test.rb @@ -35,6 +35,14 @@ describe Azure::StorageManagement::Serialization do describe "#storage_services_to_xml" do let(:storage_service_name) {'storage-service'} + let(:storage_options) { { + :extended_properties=> { + :prop_1_name=>"prop_1_value" + }, + :label=>"Test Label", + :geo_replication_enabled=>true + } + } it "accepts an name and options hash" do subject.storage_services_to_xml storage_service_name @@ -61,5 +69,164 @@ describe Azure::StorageManagement::Serialization do doc.css('AffinityGroup').text.must_equal 'my-affinity-group' results.must_be_kind_of String end + + it 'serializes geo_replication_enabled and extended_properties to xml' do + results = subject.storage_services_to_xml( + storage_service_name, storage_options + ) + + doc = Nokogiri::XML(results) + doc.css('GeoReplicationEnabled').text.must_equal( + "#{storage_options[:geo_replication_enabled]}" + ) + doc.css('ExtendedProperties ExtendedProperty').size.must_equal(1) + doc.css('ExtendedProperties ExtendedProperty').first.css( + 'Name' + ).text.must_equal('prop_1_name') + doc.css('ExtendedProperties ExtendedProperty').first.css( + 'Value' + ).text.must_equal(storage_options[:extended_properties][:prop_1_name]) + end + + it 'uses provided label instead of name for Label' do + options = { + label: 'My Label' + } + results = subject.storage_services_to_xml(storage_service_name, options) + doc = Nokogiri::XML(results) + Base64.decode64(doc.css('Label').text).must_equal(options[:label]) + end + + it 'uses name for Label when label is not provided' do + results = subject.storage_services_to_xml(storage_service_name) + doc = Nokogiri::XML(results) + Base64.decode64(doc.css('Label').text).must_equal(storage_service_name) + end end -end \ No newline at end of file + + describe "#storage_update_to_xml" do + nil_options = { + description: nil, + label: nil, + geo_replication_enabled: 'nil', + extended_properties: nil + } + + let(:empty_options) { { + description: '', + label: '', + geo_replication_enabled: '', + extended_properties: '' + } + } + + let(:partial_options_gre_bool) { { + label: 'label', + geo_replication_enabled: false + } + } + + let(:partial_options_gre_string) { { + description: 'label', + geo_replication_enabled: 'false' + } + } + + let(:options) { { + extended_properties: { + prop_1_name: 'prop_1_value' + }, + description: 'Test Description', + label: 'Test Label', + geo_replication_enabled: true + } + } + + let(:no_options_message) {'No options specified'} + let(:label_or_desc_required) { 'Either one of Label or Description has to be provided. Both cannot be empty' } + + it "checks if the parameter is nil" do + exception = assert_raises RuntimeError do + subject.storage_update_to_xml nil + end + assert_match no_options_message, exception.message + end + + it "checks if the parameter is empty" do + exception = assert_raises RuntimeError do + subject.storage_update_to_xml Hash.new + end + assert_match no_options_message, exception.message + end + + it "checks if all options are nil" do + exception = assert_raises RuntimeError do + subject.storage_update_to_xml nil_options + end + assert_match label_or_desc_required, exception.message + end + + it "checks if all options are empty" do + exception = assert_raises RuntimeError do + subject.storage_update_to_xml empty_options + end + assert_match label_or_desc_required, exception.message + end + + it "returns xml for the partial options provided" do + storage_update_xml_string = subject.storage_update_to_xml partial_options_gre_bool + storage_update_xml_string.must_be_kind_of String + + storage_update_xml = Nokogiri::XML storage_update_xml_string + storage_update_xml.css('Label').text.must_equal(Base64::encode64(partial_options_gre_bool[:label])) + storage_update_xml.css('GeoReplicationEnabled').text.must_equal "#{partial_options_gre_bool[:geo_replication_enabled]}" + + storage_update_xml.css('Description').size.must_equal 0 + storage_update_xml.css('ExtendedProperties').size.must_equal 0 + end + + it "returns xml for the partial options provided without gre" do + storage_update_xml_string = subject.storage_update_to_xml partial_options_gre_string + storage_update_xml_string.must_be_kind_of String + + storage_update_xml = Nokogiri::XML storage_update_xml_string + storage_update_xml.css('Description').size.must_equal 1 + storage_update_xml.css('Description').text.must_equal partial_options_gre_string[:description] + + storage_update_xml.css('GeoReplicationEnabled').size.must_equal 0 + + storage_update_xml.css('ExtendedProperties').size.must_equal 0 + end + + it "returns xml for the options provided" do + update_xml_string = subject.storage_update_to_xml options + update_xml_string.must_be_kind_of String + + update_xml = Nokogiri::XML update_xml_string + + update_xml.css('Label').text.must_equal(Base64::encode64(options[:label])) + update_xml.css('GeoReplicationEnabled').size.must_equal 1 + update_xml.css('GeoReplicationEnabled').text.must_equal "#{options[:geo_replication_enabled]}" + + update_xml.css('ExtendedProperties ExtendedProperty').size.must_equal 1 + update_xml.css('ExtendedProperties ExtendedProperty Name').text.must_equal "#{:prop_1_name}" + update_xml.css('ExtendedProperties ExtendedProperty Value').text.must_equal options[:extended_properties][:prop_1_name] + end + + it "returns xml for options but with description in absence of label" do + options.delete(:label) + update_xml_string = subject.storage_update_to_xml(options) + update_xml_string.must_be_kind_of(String) + + update_xml = Nokogiri::XML(update_xml_string) + + update_xml.css('Description').text.must_equal(options[:description]) + update_xml.css('GeoReplicationEnabled').size.must_equal(1) + update_xml.css('ExtendedProperties ExtendedProperty').size.must_equal(1) + update_xml.css('ExtendedProperties ExtendedProperty Name').text + .must_equal("#{:prop_1_name}") + update_xml.css('ExtendedProperties ExtendedProperty Value') + .text.must_equal(options[:extended_properties][:prop_1_name]) + end + end +end diff --git a/test/unit/storage_management/storage_management_service_test.rb b/test/unit/storage_management/storage_management_service_test.rb index 177a99227..b962063b2 100644 --- a/test/unit/storage_management/storage_management_service_test.rb +++ b/test/unit/storage_management/storage_management_service_test.rb @@ -55,7 +55,6 @@ describe Azure::StorageManagementService do end describe "#get_storage_account" do - before { ManagementHttpRequest.stubs(:new).with(method, request_path, nil).returns(mock_request) mock_request.expects(:call).returns(response_body) @@ -77,6 +76,33 @@ describe Azure::StorageManagementService do end describe "#create_storage_account" do + let(:options) { + { + location: 'East Asia', + description: 'Test Storage Description' + } + } + + it 'Checks if name is provided' do + exception = assert_raises(RuntimeError) do + subject.create_storage_account(options) + end + assert_match('Name not specified', exception.message) + end + + it 'Checks if name is not nil' do + exception = assert_raises(RuntimeError) do + subject.create_storage_account('', options) + end + assert_match('Name not specified', exception.message) + end + + it 'Checks if name is not empty' do + exception = assert_raises(RuntimeError) do + subject.create_storage_account('', options) + end + assert_match('Name not specified', exception.message) + end it "Create storage account return message if storage account exists of given name." do ManagementHttpRequest.any_instance.expects(:call).returns response_body @@ -91,4 +117,117 @@ describe Azure::StorageManagementService do end end + + describe "#update_storage_account" do + let(:updated_accounts_xml) { Fixtures["updated_storage_accounts"] } + let(:no_options_specified) { 'No options specified' } + let(:update_storage_account_req) { Fixtures["update_storage_account"] } + + let(:updated_storage_account_mock_request){ mock() } + let(:updated_storage_account_response) { + updated_storage_account_response = mock() + updated_storage_account_response.stubs(:body).returns(updated_accounts_xml) + updated_storage_account_response + } + let(:updated_storage_account_response_body) {Nokogiri::XML updated_storage_account_response.body} + + let(:update_request) { + update_request = mock() + update_request.stubs(:body).returns('') + update_request + } + + before { + ManagementHttpRequest.stubs(:new).with(method, request_path, nil).returns(mock_request) + mock_request.expects(:call).returns(response_body) + + Azure::StorageManagement::Serialization.stubs(:update_storage_account).returns(update_storage_account_req) + } + + let(:options) { { + description: 'Test Description', + label: 'Test Label', + geo_replication_enabled: false, + extended_properties: { + prop_1_name: 'prop_1_value', + prop_2_name: 'prop_2_value' + } + } + } + + it "checks if nil options is provided" do + exception = assert_raises RuntimeError do + subject.update_storage_account 'storage2', nil + end + assert_match no_options_specified, exception.message + end + + it "checks if empty options is provided" do + exception = assert_raises RuntimeError do + subject.update_storage_account 'storage2', Hash.new + end + assert_match no_options_specified, exception.message + end + + it "checks if account exists before updating" do + ret_val = subject.update_storage_account 'storage3', Hash.new + ret_val.must_equal "Storage Account 'storage3' does not exist. Skipped..." + end + + it "updates the specified account" do + ManagementHttpRequest.stubs(:new).with(:put, "#{request_path}/storage2", update_storage_account_req).returns(update_request) + update_request.expects(:call).returns('') + + subject.update_storage_account 'storage2', options + + ManagementHttpRequest.stubs(:new).with(method, request_path, nil).returns(updated_storage_account_mock_request) + updated_storage_account_mock_request.expects(:call).returns(updated_storage_account_response_body) + + accounts = subject.list_storage_accounts + + accounts.each { |account| + next unless account.name == 'storage2' + + account.name.must_equal 'storage2' + account.label.must_equal options[:label] + account.geo_replication_enabled.must_equal "#{options[:geo_replication_enabled]}" + + account.extended_properties.each { |prop| + prop[:value].must_equal "#{options[:extended_properties][:"#{prop[:name]}"]}" + } + } + end + end + + describe '#get_storage_account_properties' do + let(:account_name) { 'storage2' } + let(:label) { 'ValidLabel' } + let(:request_path) { "/services/storageservices/#{account_name}" } + let(:account_xml) { Fixtures['get_storage_account_properties'] } + + let(:get_account_mock_request) { mock() } + let(:get_account_response) { + get_account_response = mock() + get_account_response.stubs(:body).returns(account_xml) + get_account_response + } + let(:get_account_response_body) { + Nokogiri::XML(get_account_response.body) + } + + before { + ManagementHttpRequest.stubs(:new).with( + :get, request_path, nil + ).returns(get_account_mock_request) + get_account_mock_request.expects(:call).returns( + get_account_response_body + ) + } + + it 'returns the label as a Base64 decoded string' do + account = subject.get_storage_account_properties(account_name) + account.label.must_be_kind_of(String) + account.label.must_equal(label) + end + end end