class SwiftClient

Constants

VERSION

Attributes

auth_token[RW]
cache_store[RW]
options[RW]
storage_url[RW]

Public Class Methods

new(options = {}) click to toggle source
# File lib/swift_client.rb, line 31
def initialize(options = {})
  raise(OptionError, "Setting expires_in connection wide is deprecated") if options[:expires_in]

  self.options = options
  self.cache_store = options[:cache_store] || SwiftClient::NullCache.new

  authenticate
end

Public Instance Methods

bulk_delete(items, options = {}) click to toggle source
# File lib/swift_client.rb, line 172
def bulk_delete(items, options = {})
  items.each_slice(1_000) do |slice|
    request :delete, "/?bulk-delete", options.merge(:body => slice.join("\n"), :headers => { "Content-Type" => "text/plain" })
  end

  items
end
delete_container(container_name, options = {}) click to toggle source
# File lib/swift_client.rb, line 88
def delete_container(container_name, options = {})
  raise(EmptyNameError) if container_name.empty?

  request :delete, "/#{container_name}", options
end
delete_object(object_name, container_name, options = {}) click to toggle source
# File lib/swift_client.rb, line 138
def delete_object(object_name, container_name, options = {})
  raise(EmptyNameError) if object_name.empty? || container_name.empty?

  request :delete, "/#{container_name}/#{object_name}", options
end
get_container(container_name, query = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 60
def get_container(container_name, query = {}, options = {})
  raise(EmptyNameError) if container_name.empty?

  request :get, "/#{container_name}", options.merge(:query => query)
end
get_containers(query = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 52
def get_containers(query = {}, options = {})
  request :get, "/", options.merge(:query => query)
end
get_object(object_name, container_name, options = {}, &block) click to toggle source
# File lib/swift_client.rb, line 120
def get_object(object_name, container_name, options = {}, &block)
  raise(EmptyNameError) if object_name.empty? || container_name.empty?

  request(:get, "/#{container_name}/#{object_name}", options.merge(block ? { :stream_body => true } : {}), &block)
end
get_objects(container_name, query = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 144
def get_objects(container_name, query = {}, options = {})
  raise(EmptyNameError) if container_name.empty?

  request :get, "/#{container_name}", options.merge(:query => query)
end
head_account(options = {}) click to toggle source
# File lib/swift_client.rb, line 40
def head_account(options = {})
  request :head, "/", options
end
head_container(container_name, options = {}) click to toggle source
# File lib/swift_client.rb, line 70
def head_container(container_name, options = {})
  raise(EmptyNameError) if container_name.empty?

  request :head, "/#{container_name}", options
end
head_containers(options = {}) click to toggle source
# File lib/swift_client.rb, line 48
def head_containers(options = {})
  request :head, "/", options
end
head_object(object_name, container_name, options = {}) click to toggle source
# File lib/swift_client.rb, line 126
def head_object(object_name, container_name, options = {})
  raise(EmptyNameError) if object_name.empty? || container_name.empty?

  request :head, "/#{container_name}/#{object_name}", options
end
paginate_container(container_name, query = {}, options = {}, &block) click to toggle source
# File lib/swift_client.rb, line 66
def paginate_container(container_name, query = {}, options = {}, &block)
  paginate(:get_container, container_name, query, options, &block)
end
paginate_containers(query = {}, options = {}, &block) click to toggle source
# File lib/swift_client.rb, line 56
def paginate_containers(query = {}, options = {}, &block)
  paginate(:get_containers, query, options, &block)
end
paginate_objects(container_name, query = {}, options = {}, &block) click to toggle source
# File lib/swift_client.rb, line 150
def paginate_objects(container_name, query = {}, options = {}, &block)
  paginate(:get_objects, container_name, query, options, &block)
end
post_account(headers = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 44
def post_account(headers = {}, options = {})
  request :post, "/", options.merge(:headers => headers)
end
post_container(container_name, headers = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 82
def post_container(container_name, headers = {}, options = {})
  raise(EmptyNameError) if container_name.empty?

  request :post, "/#{container_name}", options.merge(:headers => headers)
end
post_head(object_name, container_name, _headers = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 132
def post_head(object_name, container_name, _headers = {}, options = {})
  raise(EmptyNameError) if object_name.empty? || container_name.empty?

  request :post, "/#{container_name}/#{object_name}", options.merge(headers: _headers)
end
post_object(object_name, container_name, headers = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 114
def post_object(object_name, container_name, headers = {}, options = {})
  raise(EmptyNameError) if object_name.empty? || container_name.empty?

  request :post, "/#{container_name}/#{object_name}", options.merge(:headers => headers)
end
public_url(object_name, container_name) click to toggle source
# File lib/swift_client.rb, line 154
def public_url(object_name, container_name)
  raise(EmptyNameError) if object_name.empty? || container_name.empty?

  "#{storage_url}/#{container_name}/#{object_name}"
end
put_container(container_name, headers = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 76
def put_container(container_name, headers = {}, options = {})
  raise(EmptyNameError) if container_name.empty?

  request :put, "/#{container_name}", options.merge(:headers => headers)
end
put_object(object_name, data_or_io, container_name, headers = {}, options = {}) click to toggle source
# File lib/swift_client.rb, line 94
def put_object(object_name, data_or_io, container_name, headers = {}, options = {})
  raise(EmptyNameError) if object_name.empty? || container_name.empty?

  mime_type = MIME::Types.of(object_name).first

  extended_headers = (headers || {}).dup

  unless find_header_key(extended_headers, "Content-Type")
    extended_headers["Content-Type"] = mime_type.content_type if mime_type
    extended_headers["Content-Type"] ||= "application/octet-stream"
  end

  if extended_headers["Transfer-Encoding"] == "identity"
    request :put, "/#{container_name}/#{object_name}", options.merge(:body => data_or_io.respond_to?(:read) ? data_or_io.read : data_or_io, :headers => extended_headers)
  else
    extended_headers["Transfer-Encoding"] = "chunked"
    request :put, "/#{container_name}/#{object_name}", options.merge(:body_stream => data_or_io.respond_to?(:read) ? data_or_io : StringIO.new(data_or_io), :headers => extended_headers)
  end
end
temp_url(object_name, container_name, opts = {}) click to toggle source
# File lib/swift_client.rb, line 160
def temp_url(object_name, container_name, opts = {})
  raise(EmptyNameError) if object_name.empty? || container_name.empty?
  raise(TempUrlKeyMissing) unless options[:temp_url_key]

  expires = (Time.now + (opts[:expires_in] || 3600).to_i).to_i
  path = URI.parse("#{storage_url}/#{container_name}/#{object_name}").path

  signature = OpenSSL::HMAC.hexdigest("sha1", options[:temp_url_key], "GET\n#{expires}\n#{path}")

  "#{storage_url}/#{container_name}/#{object_name}?temp_url_sig=#{signature}&temp_url_expires=#{expires}"
end

Private Instance Methods

authenticate() click to toggle source
# File lib/swift_client.rb, line 217
def authenticate
  return if authenticate_from_cache

  return authenticate_v3 if options[:auth_url] =~ /v3/
  return authenticate_v2 if options[:auth_url] =~ /v2/

  authenticate_v1
end
authenticate_from_cache() click to toggle source
# File lib/swift_client.rb, line 226
def authenticate_from_cache
  cached_auth_token = cache_store.get("swift_client:auth_token:#{cache_key}")
  cached_storage_url = cache_store.get("swift_client:storage_url:#{cache_key}")

  return false if cached_auth_token.nil? || cached_storage_url.nil?

  if cached_auth_token != auth_token || cached_storage_url != storage_url
    self.auth_token = cached_auth_token
    self.storage_url = cached_storage_url

    return true
  end

  false
end
authenticate_v1() click to toggle source
# File lib/swift_client.rb, line 250
def authenticate_v1
  [:auth_url, :username, :api_key].each do |key|
    raise(AuthenticationError, "#{key} missing") unless options[key]
  end

  response = HTTParty.get(options[:auth_url], :headers => { "X-Auth-User" => options[:username], "X-Auth-Key" => options[:api_key] })

  raise(AuthenticationError, "#{response.code}: #{response.message}") unless response.success?

  set_authentication_details response.headers["X-Auth-Token"], options[:storage_url] || response.headers["X-Storage-Url"]
end
authenticate_v2() click to toggle source
# File lib/swift_client.rb, line 262
def authenticate_v2
  [:auth_url, :storage_url].each do |key|
    raise(AuthenticationError, "#{key} missing") unless options[key]
  end

  auth = { "auth" => {} }

  if options[:tenant_name]
    auth["auth"]["tenantName"] = options[:tenant_name]
  else
    raise AuthenticationError, "No tenant specified"
  end

  if options[:username] && options[:password]
    auth["auth"]["passwordCredentials"] = { "username" => options[:username], "password" => options[:password] }
  elsif options[:access_key] && options[:secret_key]
    auth["auth"]["apiAccessKeyCredentials"] = { "accessKey" => options[:access_key], "secretKey" => options[:secret_key] }
  else
    raise AuthenticationError, "Unknown authentication method"
  end

  response = HTTParty.post("#{options[:auth_url].gsub(/\/+$/, "")}/tokens", :body => JSON.dump(auth), :headers => { "Content-Type" => "application/json" })

  raise(AuthenticationError, "#{response.code}: #{response.message}") unless response.success?

  set_authentication_details response.parsed_response["access"]["token"]["id"], options[:storage_url]
end
authenticate_v3() click to toggle source
# File lib/swift_client.rb, line 290
def authenticate_v3
  raise(AuthenticationError, "auth_url missing") unless options[:auth_url]
  raise(AuthenticationError, "username in combination with domain/domain_id is deprecated, please use user_domain/user_domain_id instead") if options[:username] && (options[:domain] || options[:domain_id]) && !options[:user_domain] && !options[:user_domain_id]

  auth = { "auth" => { "identity" => {} } }

  if options[:username] && options[:password] && (options[:user_domain] || options[:user_domain_id])
    auth["auth"]["identity"]["methods"] = ["password"]
    auth["auth"]["identity"]["password"] = { "user" => { "name" => options[:username], "password" => options[:password] } }
    auth["auth"]["identity"]["password"]["user"]["domain"] = options[:user_domain] ? { "name" => options[:user_domain] } : { "id" => options[:user_domain_id] }
  elsif options[:user_id] && options[:password]
    auth["auth"]["identity"]["methods"] = ["password"]
    auth["auth"]["identity"]["password"] = { "user" => { "id" => options[:user_id], "password" => options[:password] } }
  elsif options[:token]
    auth["auth"]["identity"]["methods"] = ["token"]
    auth["auth"]["identity"]["token"] = { "id" => options[:token] }
  else
    raise AuthenticationError, "Unknown authentication method"
  end

  # handle project authentication scope

  if (options[:project_id] || options[:project_name]) && (options[:project_domain_name] || options[:project_domain_id])
    auth["auth"]["scope"] = { "project" => { "domain" => {} } }
    auth["auth"]["scope"]["project"]["name"] =  options[:project_name] if options[:project_name]
    auth["auth"]["scope"]["project"]["id"] =  options[:project_id] if options[:project_id]
    auth["auth"]["scope"]["project"]["domain"]["name"] = options[:project_domain_name] if options[:project_domain_name]
    auth["auth"]["scope"]["project"]["domain"]["id"] = options[:project_domain_id] if options[:project_domain_id]
  end

  # handle domain authentication scope

  if options[:domain_name] || options[:domain_id]
    auth["auth"]["scope"] = { "domain" => {} }
    auth["auth"]["scope"]["domain"]["name"] = options[:domain_name] if options[:domain_name]
    auth["auth"]["scope"]["domain"]["id"] = options[:domain_id] if options[:domain_id]
  end

  response = HTTParty.post("#{options[:auth_url].gsub(/\/+$/, "")}/auth/tokens", :body => JSON.dump(auth), :headers => { "Content-Type" => "application/json" })

  raise(AuthenticationError, "#{response.code}: #{response.message}") unless response.success?

  storage_url = options[:storage_url] || storage_url_from_v3_response(response)

  raise(AuthenticationError, "storage_url missing") unless storage_url

  set_authentication_details response.headers["X-Subject-Token"], storage_url
end
cache_key() click to toggle source
# File lib/swift_client.rb, line 182
def cache_key
  auth_keys = [:auth_url, :username, :access_key, :user_id, :user_domain, :user_domain_id, :domain_name,
    :domain_id, :token, :project_id, :project_name, :project_domain_name, :project_domain_id, :tenant_name]

  auth_key = auth_keys.collect { |key| options[key] }.inspect

  Digest::SHA1.hexdigest(auth_key)
end
find_header_key(headers, key) click to toggle source
# File lib/swift_client.rb, line 191
def find_header_key(headers, key)
  headers.keys.detect { |k| k.downcase == key.downcase }
end
paginate(method, *args, query, options) { |response| ... } click to toggle source
# File lib/swift_client.rb, line 354
def paginate(method, *args, query, options)
  return enum_for(:paginate, method, *args, query, options) unless block_given?

  marker = nil

  loop do
    response = send(method, *args, marker ? query.merge(:marker => marker) : query, options)

    return if response.parsed_response.empty?

    yield response

    marker = response.parsed_response.last["name"]
  end
end
request(method, path, opts = {}, &block) click to toggle source
# File lib/swift_client.rb, line 195
def request(method, path, opts = {}, &block)
  headers = (opts[:headers] || {}).dup
  headers["X-Auth-Token"] = auth_token
  headers["Accept"] ||= "application/json" unless opts.delete(:json) == false

  stream_pos = opts[:body_stream].pos if opts[:body_stream]

  response = HTTParty.send(method, "#{storage_url}#{path}", opts.merge(:headers => headers), &block)

  if response.code == 401
    authenticate

    opts[:body_stream].pos = stream_pos if opts[:body_stream]

    return request(method, path, opts, &block)
  end

  raise(ResponseError.new(response.code, response.message)) unless response.success?

  response
end
set_authentication_details(auth_token, storage_url) click to toggle source
# File lib/swift_client.rb, line 242
def set_authentication_details(auth_token, storage_url)
  cache_store.set("swift_client:auth_token:#{cache_key}", auth_token)
  cache_store.set("swift_client:storage_url:#{cache_key}", storage_url)

  self.auth_token = auth_token
  self.storage_url = storage_url
end
storage_url_from_v3_response(response) click to toggle source
# File lib/swift_client.rb, line 339
def storage_url_from_v3_response(response)
  swift_services = Array(response.parsed_response["token"]["catalog"]).select { |service| service["type"] == "object-store" }
  swift_service = swift_services.first

  return unless swift_services.size == 1

  interface = options[:interface] || "public"
  swift_endpoints = swift_service["endpoints"].select { |endpoint| endpoint["interface"] == interface }
  swift_endpoint = swift_endpoints.first

  return unless swift_endpoints.size == 1

  swift_endpoint["url"]
end