1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322
|
# frozen_string_literal: true
#-------------------------------------------------------------------------
# # Copyright (c) Microsoft and contributors. All rights reserved.
#
# The MIT License(MIT)
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files(the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions :
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#--------------------------------------------------------------------------
require "azure/core/signed_service"
require "azure/storage/common/core"
require "azure/storage/common/service/storage_service_properties"
require "azure/storage/common/service/storage_service_stats"
module Azure::Storage::Common
module Service
# A base class for StorageService implementations
class StorageService < Azure::Core::SignedService
# @!attribute storage_service_host
# @return [Hash] Get or set the storage service host
attr_accessor :storage_service_host
# Create a new instance of the StorageService
#
# @param signer [Azure::Core::Auth::Signer] An implementation of Signer used for signing requests.
# (optional, Default=Azure::Storage::CommonAuth::SharedKey.new)
# @param account_name [String] The account name (optional, Default=Azure::Storage.storage_account_name)
# @param options [Azure::Storage::CommonConfigurable] the client configuration context
def initialize(signer = nil, account_name = nil, options = {}, &block)
StorageService.register_request_callback(&block) if block_given?
client_config = options[:client]
signer = signer || Azure::Storage::Common::Core::Auth::SharedKey.new(
client_config.storage_account_name,
client_config.storage_access_key) if client_config.storage_access_key
signer = signer || Azure::Storage::Common::Core::Auth::SharedAccessSignatureSigner.new(
client_config.storage_account_name,
client_config.storage_sas_token)
@storage_service_host = { primary: "", secondary: "" };
super(signer, account_name, options)
end
def call(method, uri, body = nil, headers = {}, options = {})
super(method, uri, body, StorageService.common_headers(options, body).merge(headers), options)
end
# Public: Get Storage Service properties
#
# See http://msdn.microsoft.com/en-us/library/azure/hh452239
# See http://msdn.microsoft.com/en-us/library/azure/hh452243
#
# ==== Options
#
# * +:timeout+ - Integer. A timeout in seconds.
# * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
# in the analytics logs when storage analytics logging is enabled.
#
# Returns a Hash with the service properties or nil if the operation failed
def get_service_properties(options = {})
query = {}
StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
response = call(:get, service_properties_uri(query), nil, {}, options)
Serialization.service_properties_from_xml response.body
end
# Public: Set Storage Service properties
#
# service_properties - An instance of Azure::Storage::CommonService::StorageServiceProperties
#
# See http://msdn.microsoft.com/en-us/library/azure/hh452235
# See http://msdn.microsoft.com/en-us/library/azure/hh452232
#
# ==== Options
#
# * +:timeout+ - Integer. A timeout in seconds.
# * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
# in the analytics logs when storage analytics logging is enabled.
#
# Returns boolean indicating success.
def set_service_properties(service_properties, options = {})
query = {}
StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
body = Serialization.service_properties_to_xml service_properties
call(:put, service_properties_uri(query), body, {}, options)
nil
end
# Public: Retrieves statistics related to replication for the service.
# It is only available on the secondary location endpoint when read-access geo-redundant
# replication is enabled for the storage account.
#
# See https://docs.microsoft.com/en-us/rest/api/storageservices/get-blob-service-stats
# See https://docs.microsoft.com/en-us/rest/api/storageservices/get-queue-service-stats
# See https://docs.microsoft.com/en-us/rest/api/storageservices/get-table-service-stats
#
# ==== Options
#
# * +:timeout+ - Integer. A timeout in seconds.
# * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
# in the analytics logs when storage analytics logging is enabled.
#
# Returns a Hash with the service statistics or nil if the operation failed
def get_service_stats(options = {})
query = {}
StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
options.update(
location_mode: LocationMode::SECONDARY_ONLY,
request_location_mode: RequestLocationMode::SECONDARY_ONLY)
response = call(:get, service_stats_uri(query, options), nil, {}, options)
Serialization.service_stats_from_xml response.body
end
# Public: Generate the URI for the service properties
#
# * +:query+ - see Azure::Storage::CommonServices::GetServiceProperties#call documentation.
#
# Returns a URI.
def service_properties_uri(query = {})
query.update(restype: "service", comp: "properties")
generate_uri("", query)
end
# Public: Generate the URI for the service statistics
#
# * +:query+ - see Azure::Storage::CommonServices::GetServiceStats#call documentation.
#
# Returns a URI.
def service_stats_uri(query = {}, options = {})
query.update(restype: "service", comp: "stats")
generate_uri("", query, options)
end
# Overrides the base class implementation to determine the request uri
#
# path - String. the request path
# query - Hash. the query parameters
#
# ==== Options
#
# * +:encode+ - bool. Specifies whether to encode the path.
# * +:location_mode+ - LocationMode. Specifies the location mode used to decide
# which location the request should be sent to.
# * +:request_location_mode+ - RequestLocationMode. Specifies the location used to indicate
# which location the operation (REST API) can be performed against.
# This is determined by the API and cannot be specified by the users.
#
# Returns the uri hash
def generate_uri(path = "", query = {}, options = {})
location_mode =
if options[:location_mode].nil?
LocationMode::PRIMARY_ONLY
else
options[:location_mode]
end
request_location_mode =
if options[:request_location_mode].nil?
RequestLocationMode::PRIMARY_ONLY
else
request_location_mode = options[:request_location_mode]
end
location = StorageService.get_location location_mode, request_location_mode
if self.client.is_a?(Azure::Storage::Common::Client) && self.client.options[:use_path_style_uri]
account_path = get_account_path location
path = path.length > 0 ? account_path + "/" + path : account_path
end
@host = location == StorageLocation::PRIMARY ? @storage_service_host[:primary] : @storage_service_host[:secondary]
encode = options[:encode].nil? ? false : options[:encode]
if encode
path = CGI.escape(path.encode("UTF-8"))
# decode the forward slashes to match what the server expects.
path = path.gsub(/%2F/, "/")
# decode the backward slashes to match what the server expects.
path = path.gsub(/%5C/, "/")
# Re-encode the spaces (encoded as space) to the % encoding.
path = path.gsub(/\+/, "%20")
end
@host = storage_service_host[:primary]
options[:primary_uri] = super path, query
@host = storage_service_host[:secondary]
options[:secondary_uri] = super path, query
if location == StorageLocation::PRIMARY
@host = @storage_service_host[:primary]
return options[:primary_uri]
else
@host = @storage_service_host[:secondary]
return options[:secondary_uri]
end
end
# Get account path according to the location settings.
#
# * +:location+ - StorageLocation. Specifies the request location.
#
# Returns the account path
def get_account_path(location)
if location == StorageLocation::PRIMARY
self.client.options[:storage_account_name]
else
self.client.options[:storage_account_name] + "-secondary"
end
end
class << self
# @!attribute user_agent_prefix
# @return [Proc] Get or set the user agent prefix
attr_accessor :user_agent_prefix
# @!attribute request_callback
# @return [Proc] The callback before the request is signed and sent
attr_reader :request_callback
# Registers the callback when sending the request
# The headers in the request can be viewed or changed in the code block
def register_request_callback
@request_callback = Proc.new
end
# Get the request location.
#
# * +:location_mode+ - LocationMode. Specifies the location mode used to decide
# which location the request should be sent to.
# * +:request_location_mode+ - RequestLocationMode. Specifies the location used to indicate
# which location the operation (REST API) can be performed against.
# This is determined by the API and cannot be specified by the users.
#
# Returns the reqeust location
def get_location(location_mode, request_location_mode)
if request_location_mode == RequestLocationMode::PRIMARY_ONLY && location_mode == LocationMode::SECONDARY_ONLY
raise InvalidOptionsError, "This operation can only be executed against the primary storage location."
end
if request_location_mode == RequestLocationMode::SECONDARY_ONLY && location_mode == LocationMode::PRIMARY_ONLY
raise InvalidOptionsError, "This operation can only be executed against the secondary storage location."
end
if request_location_mode == RequestLocationMode::PRIMARY_ONLY
return StorageLocation::PRIMARY
elsif request_location_mode == RequestLocationMode::SECONDARY_ONLY
return StorageLocation::SECONDARY
end
if location_mode == LocationMode::PRIMARY_ONLY || location_mode == LocationMode::PRIMARY_THEN_SECONDARY
StorageLocation::PRIMARY
elsif location_mode == LocationMode::SECONDARY_ONLY || location_mode == LocationMode::SECONDARY_THEN_PRIMARY
StorageLocation::SECONDARY
end
end
# Adds metadata properties to header hash with required prefix
#
# * +:metadata+ - A Hash of metadata name/value pairs
# * +:headers+ - A Hash of HTTP headers
def add_metadata_to_headers(metadata, headers)
if metadata
metadata.each do |key, value|
headers["x-ms-meta-#{key}"] = value
end
end
end
# Adds a value to the Hash object
#
# * +:object+ - A Hash object
# * +:key+ - The key name
# * +:value+ - The value
def with_value(object, key, value)
object[key] = value.to_s if value
end
# Adds a header with the value
#
# * +:headers+ - A Hash of HTTP headers
# * +:name+ - The header name
# * +:value+ - The value
alias with_header with_value
# Adds a query parameter
#
# * +:query+ - A Hash of HTTP query
# * +:name+ - The parameter name
# * +:value+ - The value
alias with_query with_value
# Declares a default hash object for request headers
def common_headers(options = {}, body = nil)
headers = {}
headers.merge!("x-ms-client-request-id" => options[:request_id]) if options[:request_id]
@request_callback.call(headers) if @request_callback
headers
end
end
end
end
end
|