diff --git a/fog-azure-rm.gemspec b/fog-azure-rm.gemspec index c601f06b2..173be9f73 100755 --- a/fog-azure-rm.gemspec +++ b/fog-azure-rm.gemspec @@ -32,7 +32,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'azure_mgmt_traffic_manager', '~> 0.9.0' spec.add_dependency 'azure_mgmt_sql', '~> 0.9.0' spec.add_dependency 'azure_mgmt_key_vault', '~> 0.9.0' - spec.add_dependency 'azure-storage', '= 0.11.5.preview' + spec.add_dependency 'azure-storage', '>= 0.11.5.preview', '< 1.0' spec.add_dependency 'vhd', '0.0.4' - spec.add_dependency 'mime-types', '~> 1.25' + spec.add_dependency 'mime-types', '~> 3.0' end diff --git a/lib/fog/azurerm/constants.rb b/lib/fog/azurerm/constants.rb index ae3969906..b8d36416c 100755 --- a/lib/fog/azurerm/constants.rb +++ b/lib/fog/azurerm/constants.rb @@ -30,6 +30,7 @@ ENVIRONMENT_AZURE_CHINA_CLOUD = 'AzureChinaCloud'.freeze ENVIRONMENT_AZURE_US_GOVERNMENT = 'AzureUSGovernment'.freeze ENVIRONMENT_AZURE_GERMAN_CLOUD = 'AzureGermanCloud'.freeze +ENVIRONMENT_AZURE_STACK = 'AzureStack'.freeze # MsRestAzure::AzureOperationError class Error Codes ERROR_CODE_RESOURCE_NOT_FOUND = 'ResourceNotFound'.freeze diff --git a/lib/fog/azurerm/storage.rb b/lib/fog/azurerm/storage.rb index 31014fab8..76e0d7367 100644 --- a/lib/fog/azurerm/storage.rb +++ b/lib/fog/azurerm/storage.rb @@ -12,6 +12,7 @@ class AzureRM < Fog::Service # Recognizes when creating data client recognizes :azure_storage_account_name recognizes :azure_storage_access_key + recognizes :azure_storage_dns_suffix recognizes :debug request_path 'fog/azurerm/requests/storage' @@ -120,10 +121,20 @@ def initialize(options) @azure_storage_account_name = options[:azure_storage_account_name] @azure_storage_access_key = options[:azure_storage_access_key] + @azure_storage_dns_suffix = options[:azure_storage_dns_suffix] - azure_client = Azure::Storage::Client.create(storage_account_name: @azure_storage_account_name, - storage_access_key: @azure_storage_access_key) - azure_client.storage_blob_host = get_blob_endpoint(@azure_storage_account_name, true, @environment) + client_options = { + storage_account_name: @azure_storage_account_name, + storage_access_key: @azure_storage_access_key, + user_agent_prefix: telemetry + } + client_options[:storage_dns_suffix] = if @environment == ENVIRONMENT_AZURE_STACK + @azure_storage_dns_suffix.nil? ? 'local.azurestack.external' : @azure_storage_dns_suffix + else + storage_endpoint_suffix(@environment)[1..-1] + end + + azure_client = Azure::Storage::Client.create(client_options) @blob_client = azure_client.blob_client @blob_client.with_filter(Azure::Storage::Core::Filter::ExponentialRetryPolicyFilter.new) @blob_client.with_filter(Azure::Core::Http::DebugFilter.new) if @debug diff --git a/test/integration/blob.rb b/test/integration/blob.rb index 66626b38e..bed13a43d 100644 --- a/test/integration/blob.rb +++ b/test/integration/blob.rb @@ -8,11 +8,14 @@ azure_credentials = YAML.load_file(File.expand_path('credentials/azure.yml', __dir__)) +location = azure_credentials['location'] + rs = Fog::Resources::AzureRM.new( tenant_id: azure_credentials['tenant_id'], client_id: azure_credentials['client_id'], client_secret: azure_credentials['client_secret'], - subscription_id: azure_credentials['subscription_id'] + subscription_id: azure_credentials['subscription_id'], + environment: azure_credentials['environment'] ) storage = Fog::Storage::AzureRM.new( @@ -40,23 +43,27 @@ begin resource_group = rs.resource_groups.create( name: resource_group_name, - location: LOCATION + location: location ) storage_account = storage.storage_accounts.create( name: storage_account_name, - location: LOCATION, + location: location, resource_group: resource_group_name ) access_key = storage_account.get_access_keys[0].value Fog::Logger.debug access_key.inspect - storage_data = Fog::Storage.new( + + options = { provider: 'AzureRM', azure_storage_account_name: storage_account.name, azure_storage_access_key: access_key, environment: azure_credentials['environment'] - ) + } + options[:storage_dns_suffix] = azure_credentials['azure_storage_dns_suffix'] unless azure_credentials['azure_storage_dns_suffix'].nil? + + storage_data = Fog::Storage.new(options) ######################################################################################################################## ###################### Create Container ###################### diff --git a/test/integration/blob_azure_stack.rb b/test/integration/blob_azure_stack.rb new file mode 100644 index 000000000..6b67f04b9 --- /dev/null +++ b/test/integration/blob_azure_stack.rb @@ -0,0 +1,277 @@ +require 'fog/azurerm' +require 'yaml' + +######################################################################################################################## +###################### Services object required by all actions ###################### +###################### Keep it Uncommented! ###################### +######################################################################################################################## + +azure_credentials = YAML.load_file(File.expand_path('credentials/azure-stack-storage.yml', __dir__)) + +######################################################################################################################## +###################### Resource names ##################### +######################################################################################################################## + +time = current_time +storage_account_name = azure_credentials['storage_account_name'] +access_key = azure_credentials['storage_account_access_key'] +container_name = "con#{time}" +test_container_name = "tcon#{time}" + +######################################################################################################################## +###################### Prerequisites ###################### +######################################################################################################################## + +begin + options = { + provider: 'AzureRM', + azure_storage_account_name: storage_account_name, + azure_storage_access_key: access_key, + environment: azure_credentials['environment'] + } + options[:azure_storage_dns_suffix] = azure_credentials['storage_dns_suffix'] unless azure_credentials['storage_dns_suffix'].nil? + + storage_data = Fog::Storage.new(options) + + ######################################################################################################################## + ###################### Create Container ###################### + ######################################################################################################################## + + storage_data.directories.create( + key: container_name, + public: true + ) + + storage_data.directories.create( + key: test_container_name, + public: false + ) + + ######################################################################################################################## + ###################### Get container ###################### + ######################################################################################################################## + + container = storage_data.directories.get(container_name) + test_container = storage_data.directories.get(test_container_name) + + ######################################################################################################################## + ###################### Create a small block blob ###################### + ######################################################################################################################## + + small_blob_name = 'small_test_file.dat' + content = Array.new(1024 * 1024) { [*'0'..'9', *'a'..'z'].sample }.join + + options = { + key: small_blob_name, + body: content + } + blob = container.files.create(options) + puts "Created small block blob: #{blob.key}" + + ######################################################################################################################## + ###################### Create a large block blob ###################### + ######################################################################################################################## + + large_blob_name = 'large_test_file.dat' + begin + large_blob_file_name = '/tmp/large_test_file.dat' + File.open(large_blob_file_name, 'w') do |large_file| + 33.times do + large_file.puts(content) + end + end + + File.open(large_blob_file_name) do |file| + options = { + key: large_blob_name, + body: file + } + blob = container.files.create(options) + puts "Created large block blob: #{blob.key}" + end + ensure + File.delete(large_blob_file_name) if File.exist?(large_blob_file_name) + end + + ######################################################################################################################## + ###################### Create a small page blob ###################### + ######################################################################################################################## + + small_page_blob_name = 'small_test_file.vhd' + content = Array.new(1024 * 1024 + 512) { [*'0'..'9', *'a'..'z'].sample }.join + + options = { + key: small_page_blob_name, + body: content, + blob_type: 'PageBlob' + } + blob = container.files.create(options) + puts "Created small page blob: #{blob.key}" + + ######################################################################################################################## + ###################### Create a large page blob ###################### + ######################################################################################################################## + + begin + large_page_blob_file_name = '/tmp/large_test_file.vhd' + large_page_blob_name = 'large_test_file.vhd' + File.open(large_page_blob_file_name, 'w') do |large_file| + content = Array.new(1024 * 1024) { [*'0'..'9', *'a'..'z'].sample }.join + 33.times do + large_file.puts(content) + end + content = Array.new(512) { [*'0'..'9', *'a'..'z'].sample }.join + large_file.puts(content) + large_file.truncate(33 * 1024 * 1024 + 512) + end + + File.open(large_page_blob_file_name) do |file| + options = { + key: large_page_blob_name, + body: file, + blob_type: 'PageBlob' + } + blob = container.files.create(options) + puts "Created large page blob: #{blob.key}" + end + ensure + File.delete(large_page_blob_file_name) if File.exist?(large_page_blob_file_name) + end + + ######################################################################################################################## + ###################### Copy Blob ######################## + ######################################################################################################################## + + puts "Copy blob: #{container.files.head(small_blob_name).copy(test_container_name, small_blob_name)}" + + ######################################################################################################################## + ###################### Get a public URL ###################### + ######################################################################################################################## + + blob_uri = container.files.head(large_blob_name).public_url + puts "Get blob public uri: #{blob_uri}" + + ######################################################################################################################## + ###################### Copy Blob from URI ######################## + ######################################################################################################################## + + copied_blob = test_container.files.new(key: 'small_blob_name') + puts "Copy blob from uri: #{copied_blob.copy_from_uri(blob_uri)}" + + ######################################################################################################################## + ###################### Update blob ###################### + ######################################################################################################################## + + copied_blob.content_encoding = 'utf-8' + copied_blob.metadata = { 'owner' => 'azure' } + copied_blob.save(update_body: false) + + temp = test_container.files.head(small_blob_name) + puts 'Updated blob' + Fog::Logger.debug temp.content_encoding + Fog::Logger.debug temp.metadata + + ######################################################################################################################## + ###################### Compare Blob ##################### + ######################################################################################################################## + + puts "Compare blobs: #{storage_data.compare_container_blobs(container_name, test_container_name)}" + + ######################################################################################################################## + ###################### Blob Exist ##################### + ######################################################################################################################## + + puts 'Blob exist' if container.files.head(small_blob_name) + + ######################################################################################################################## + ###################### Blob Count in a Container ##################### + ######################################################################################################################## + + puts "Blob count in a container: #{container.files.all.length}" + + ######################################################################################################################## + ###################### List Blobs in a Container ##################### + ######################################################################################################################## + + puts 'List blobs in a container:' + container.files.each do |temp_file| + puts temp_file.key + end + + ######################################################################################################################## + ###################### Download a small blob ###################### + ######################################################################################################################## + + begin + downloaded_file_name = '/tmp/downloaded_' + small_blob_name + blob = container.files.get(small_blob_name) + File.open(downloaded_file_name, 'wb') do |file| + file.write(blob.body) + end + puts 'Downloaded small blob' + ensure + File.delete(downloaded_file_name) if File.exist?(downloaded_file_name) + end + + ######################################################################################################################## + ###################### Download a large blob ###################### + ######################################################################################################################## + + begin + downloaded_file_name = '/tmp/downloaded_' + large_blob_name + File.open(downloaded_file_name, 'wb') do |file| + container.files.get(large_blob_name) do |chunk, remaining_bytes, total_bytes| + Fog::Logger.debug "remaining_bytes: #{remaining_bytes}, total_bytes: #{total_bytes}" + file.write(chunk) + end + end + puts 'Downloaded large blob' + ensure + File.delete(downloaded_file_name) if File.exist?(downloaded_file_name) + end + + ######################################################################################################################## + ###################### Get a https URL with expires ###################### + ######################################################################################################################## + + test_blob = test_container.files.head(small_blob_name) + puts "Get https URL with expires: #{test_blob.public?}, #{test_blob.url(Time.now + 3600)}" + Fog::Logger.debug test_blob.public? + Fog::Logger.debug test_blob.url(Time.now + 3600) + + ######################################################################################################################## + ###################### Get a http URL with expires ###################### + ######################################################################################################################## + + puts "Get a http URL with expires: #{test_blob.url(Time.now + 3600, scheme: 'http')}" + + ######################################################################################################################## + ###################### Lease Blob ###################### + ######################################################################################################################## + + lease_id_blob = storage_data.acquire_blob_lease(container_name, large_blob_name) + puts 'Leased blob' + + ######################################################################################################################## + ###################### Release Leased Blob ###################### + ######################################################################################################################## + + storage_data.release_blob_lease(container_name, large_blob_name, lease_id_blob) + puts 'Release Leased Blob' + + ######################################################################################################################## + ###################### Delete Blob ###################### + ######################################################################################################################## + + blob = container.files.head(large_blob_name) + puts "Deleted blob: #{blob.destroy}" + + ######################################################################################################################## + ###################### Deleted Container ###################### + ######################################################################################################################## + + puts "Deleted container: #{container.destroy}" + puts "Deleted test container: #{test_container.destroy}" +rescue => ex + puts "Integration Test for blob is failing: #{ex.inspect}\n#{ex.backtrace.join("\n")}" +end diff --git a/test/integration/container.rb b/test/integration/container.rb index f22cfac76..ecb89aa24 100644 --- a/test/integration/container.rb +++ b/test/integration/container.rb @@ -8,11 +8,14 @@ azure_credentials = YAML.load_file(File.expand_path('credentials/azure.yml', __dir__)) +location = azure_credentials['location'] + rs = Fog::Resources::AzureRM.new( tenant_id: azure_credentials['tenant_id'], client_id: azure_credentials['client_id'], client_secret: azure_credentials['client_secret'], - subscription_id: azure_credentials['subscription_id'] + subscription_id: azure_credentials['subscription_id'], + environment: azure_credentials['environment'] ) storage = Fog::Storage::AzureRM.new( @@ -40,25 +43,27 @@ begin resource_group = rs.resource_groups.create( name: resource_group_name, - location: LOCATION + location: location ) storage_account_name = "sa#{current_time}" storage_account = storage.storage_accounts.create( name: storage_account_name, - location: LOCATION, + location: location, resource_group: resource_group_name ) keys = storage_account.get_access_keys access_key = keys.first.value - storage_data = Fog::Storage::AzureRM.new( + options = { azure_storage_account_name: storage_account.name, azure_storage_access_key: access_key, environment: azure_credentials['environment'] - ) + } + options[:storage_dns_suffix] = azure_credentials['azure_storage_dns_suffix'] unless azure_credentials['azure_storage_dns_suffix'].nil? + storage_data = Fog::Storage::AzureRM.new(options) ######################################################################################################################## ###################### Check Container Exists ###################### @@ -76,7 +81,7 @@ ) puts "Created container: #{container.key}" - storage_data.directories.create( + container = storage_data.directories.create( key: test_container_name, public: true ) diff --git a/test/integration/credentials/azure-stack-storage.yml b/test/integration/credentials/azure-stack-storage.yml new file mode 100644 index 000000000..7d9fc7e6a --- /dev/null +++ b/test/integration/credentials/azure-stack-storage.yml @@ -0,0 +1,4 @@ +environment: XXXXXXXX +storage_account_name: XXXXXXXX +storage_account_access_key: XXXXXXXX +storage_dns_suffix: XXXXXXXX diff --git a/test/integration/credentials/azure.yml b/test/integration/credentials/azure.yml index 39c593fe3..2264fe6de 100644 --- a/test/integration/credentials/azure.yml +++ b/test/integration/credentials/azure.yml @@ -3,3 +3,4 @@ client_secret: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX subscription_id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX tenant_id: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX environment: XXXXXXXX +location: XXX diff --git a/test/integration/storage_account.rb b/test/integration/storage_account.rb index e3a46b6c6..750e998e8 100644 --- a/test/integration/storage_account.rb +++ b/test/integration/storage_account.rb @@ -8,11 +8,14 @@ azure_credentials = YAML.load_file(File.expand_path('credentials/azure.yml', __dir__)) +location = azure_credentials['location'] + rs = Fog::Resources::AzureRM.new( tenant_id: azure_credentials['tenant_id'], client_id: azure_credentials['client_id'], client_secret: azure_credentials['client_secret'], - subscription_id: azure_credentials['subscription_id'] + subscription_id: azure_credentials['subscription_id'], + environment: azure_credentials['environment'] ) storage = Fog::Storage::AzureRM.new( @@ -41,7 +44,7 @@ begin resource_group = rs.resource_groups.create( name: resource_group_name, - location: LOCATION + location: location ) ######################################################################################################################## @@ -66,7 +69,7 @@ storage_account = storage.storage_accounts.create( name: lrs_storage_account, - location: LOCATION, + location: location, resource_group: resource_group_name, tags: tags ) @@ -78,7 +81,7 @@ storage_account = storage.storage_accounts.create( name: grs_storage_account, - location: LOCATION, + location: location, resource_group: resource_group_name, sku_name: Fog::ARM::Storage::Models::SkuTier::Standard, replication: 'GRS', @@ -93,7 +96,7 @@ storage_account = storage.storage_accounts.create( name: premium_storage_acc, - location: LOCATION, + location: location, resource_group: resource_group_name, sku_name: Fog::ARM::Storage::Models::SkuTier::Premium, replication: 'LRS',