Class: Fog::Storage::Rackspace::Mock

Inherits:
Rackspace::Service show all
Includes:
Common
Defined in:
lib/fog/rackspace/storage.rb,
lib/fog/rackspace/requests/storage/get_object.rb,
lib/fog/rackspace/requests/storage/put_object.rb,
lib/fog/rackspace/requests/storage/head_object.rb,
lib/fog/rackspace/requests/storage/delete_object.rb,
lib/fog/rackspace/requests/storage/get_container.rb,
lib/fog/rackspace/requests/storage/put_container.rb,
lib/fog/rackspace/requests/storage/get_containers.rb,
lib/fog/rackspace/requests/storage/head_container.rb,
lib/fog/rackspace/requests/storage/head_containers.rb,
lib/fog/rackspace/requests/storage/delete_container.rb,
lib/fog/rackspace/requests/storage/get_object_http_url.rb,
lib/fog/rackspace/requests/storage/put_object_manifest.rb,
lib/fog/rackspace/requests/storage/get_object_https_url.rb,
lib/fog/rackspace/requests/storage/delete_multiple_objects.rb,
lib/fog/rackspace/requests/storage/put_static_obj_manifest.rb,
lib/fog/rackspace/requests/storage/put_dynamic_obj_manifest.rb,
lib/fog/rackspace/requests/storage/delete_static_large_object.rb,
lib/fog/rackspace/requests/storage/post_set_meta_temp_url_key.rb

Defined Under Namespace

Classes: MockContainer, MockObject

Constant Summary collapse

HeaderOptions =
%w{
  Content-Type Access-Control-Allow-Origin Origin Content-Disposition
  Etag Content-Encoding
}.freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Common

#account, #apply_options, #authenticate, #cdn, #endpoint_uri, #get_object_http_url, #get_object_https_url, #put_object_manifest, #region, #request_id_header, #service_name, #service_net?

Methods inherited from Rackspace::Service

#authenticate, #endpoint_uri, #region, #request, #request_without_retry, #service_name, #service_net?

Constructor Details

#initialize(options = {}) ⇒ Mock

Returns a new instance of Mock.



343
344
345
346
347
# File 'lib/fog/rackspace/storage.rb', line 343

def initialize(options={})
  apply_options(options)
  authenticate
  endpoint_uri
end

Class Method Details

.account_metaHash<String,String>

Access or create account-wide metadata.

Returns:

  • (Hash<String,String>)

    A metadata hash pre-populated with a (fake) temp URL key.



330
331
332
333
334
335
336
# File 'lib/fog/rackspace/storage.rb', line 330

def self.
  @account_meta ||= Hash.new do |hash, key|
    hash[key] = {
      'X-Account-Meta-Temp-Url-Key' => Fog::Mock.random_hex(32)
    }
  end
end

.dataObject



320
321
322
323
324
# File 'lib/fog/rackspace/storage.rb', line 320

def self.data
  @data ||= Hash.new do |hash, key|
    hash[key] = {}
  end
end

.resetObject



338
339
340
341
# File 'lib/fog/rackspace/storage.rb', line 338

def self.reset
  @data = nil
  @account_meta = nil
end

Instance Method Details

#account_metaObject



353
354
355
# File 'lib/fog/rackspace/storage.rb', line 353

def 
  self.class.[@rackspace_username]
end

#add_container(cname) ⇒ MockContainer

Create and add a new, empty MockContainer with the given name. An existing container with the same name will be replaced.

Parameters:

  • cname (String)

    The (unescaped) container name.

Returns:



386
387
388
# File 'lib/fog/rackspace/storage.rb', line 386

def add_container(cname)
  data[Fog::Rackspace.escape(cname)] = MockContainer.new(self)
end

#dataObject



349
350
351
# File 'lib/fog/rackspace/storage.rb', line 349

def data
  self.class.data[@rackspace_username]
end

#delete_container(name) ⇒ Object



23
24
25
26
27
28
29
30
31
32
# File 'lib/fog/rackspace/requests/storage/delete_container.rb', line 23

def delete_container(name)
  c = mock_container! name

  raise Excon::Errors::Conflict.new 'Conflict' unless c.empty?
  remove_container name

  response = Excon::Response.new
  response.status = 204
  response
end

#delete_multiple_objects(container, object_names, options = {}) ⇒ Object



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
# File 'lib/fog/rackspace/requests/storage/delete_multiple_objects.rb', line 73

def delete_multiple_objects(container, object_names, options = {})
  results = {
    "Number Not Found" => 0,
    "Response Status" => "200 OK",
    "Response Body" => "",
    "Errors" => [],
    "Number Deleted" => 0
  }

  object_names.each do |name|
    if container
      cname, oname = container, name
    else
      cname, oname = name.split('/', 2)
    end

    c = mock_container cname
    if c.nil?
      # Container not found
      results["Number Not Found"] += 1
      next
    end

    if oname.nil?
      # No object name specified; delete the container if it's nonempty
      unless c.empty?
        results["Response Status"] = "400 Bad Request"
        results["Errors"] << [cname, "409 Conflict"]
        next
      end

      remove_container cname
      results["Number Deleted"] += 1
      next
    end

    o = c.mock_object oname
    if o.nil?
      # Object not found.
      results["Number Not Found"] += 1
      next
    end

    c.remove_object oname
    results["Number Deleted"] += 1
  end

  response = Excon::Response.new
  response.status = 200
  response.body = results
  response
end

#delete_object(container, object) ⇒ Object



24
25
26
27
28
29
30
31
32
# File 'lib/fog/rackspace/requests/storage/delete_object.rb', line 24

def delete_object(container, object)
  c = mock_container! container
  c.mock_object! object
  c.remove_object object

  response = Excon::Response.new
  response.status = 204
  response
end

#delete_static_large_object(container, object, options = {}) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/fog/rackspace/requests/storage/delete_static_large_object.rb', line 47

def delete_static_large_object(container, object, options = {})
  c = mock_container container
  return not_found(container) unless c

  o = c.mock_object object
  return not_found(object) unless o

  # What happens if o isn't a static large object?
  raise Fog::Storage::Rackspace::BadRequest.new unless o.static_manifest?

  segments = Fog::JSON.decode(o.body)
  paths = segments.map { |s| s['path'] }
  paths << "#{container}/#{object}"
  delete_multiple_objects(nil, paths)
end

#get_container(container, options = {}) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/fog/rackspace/requests/storage/get_container.rb', line 46

def get_container(container, options = {})
  c = mock_container! container

  results = []
  c.objects.each do |key, mock_file|
    results << {
      "hash" => mock_file.hash,
      "last_modified" => mock_file.last_modified.strftime('%Y-%m-%dT%H:%M:%S.%L'),
      "bytes" => mock_file.bytes_used,
      "name" => key,
      "content_type" => mock_file.content_type
    }
  end

  response = Excon::Response.new
  response.status = 200
  response.headers = c.to_headers
  response.body = results
  response
end

#get_containers(options = {}) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/fog/rackspace/requests/storage/get_containers.rb', line 35

def get_containers(options = {})
  results = data.map do |name, container|
    {
      "name" => name,
      "count" => container.objects.size,
      "bytes" => container.bytes_used
    }
  end
  response = Excon::Response.new
  response.status = 200
  response.body = results
  response
end

#get_object(container, object, &block) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/fog/rackspace/requests/storage/get_object.rb', line 30

def get_object(container, object, &block)
  c = mock_container! container
  o = c.mock_object! object

  body, size = "", 0

  o.each_part do |part|
    body << part.body
    size += part.bytes_used
  end

  if block_given?
    # Just send it all in one chunk.
    block.call(body, 0, size)
  end

  response = Excon::Response.new
  response.body = body
  response.headers = o.to_headers
  response
end

#head_container(container) ⇒ Object



30
31
32
33
34
35
36
37
# File 'lib/fog/rackspace/requests/storage/head_container.rb', line 30

def head_container(container)
  c = mock_container! container

  response = Excon::Response.new
  response.status = 204
  response.headers = c.to_headers
  response
end

#head_containersObject



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/fog/rackspace/requests/storage/head_containers.rb', line 27

def head_containers
  bytes_used = data.values.map { |c| c.bytes_used }.reduce(0) { |a, b| a + b }
  container_count = data.size
  object_count = data.values.map { |c| c.objects.size }.reduce(0) { |a, b| a + b }

  response = Excon::Response.new
  response.status = 204
  response.headers = {
    'X-Account-Bytes-Used' => bytes_used,
    'X-Account-Container-Count' => container_count,
    'X-Account-Object-Count' => object_count
  }.merge()
  response
end

#head_object(container, object) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/fog/rackspace/requests/storage/head_object.rb', line 24

def head_object(container, object)
  c = mock_container! container
  o = c.mock_object! object

  headers = o.to_headers

  hashes, length = [], 0
  o.each_part do |part|
    hashes << part.hash
    length += part.bytes_used
  end

  headers['Etag'] = "\"#{Digest::MD5.hexdigest(hashes.join)}\""
  headers['Content-Length'] = length.to_s
  headers['X-Static-Large-Object'] = "True" if o.static_manifest?

  response = Excon::Response.new
  response.status = 200
  response.headers = headers
  response
end

#mock_container(cname) ⇒ MockContainer?

Access a MockContainer with the specified name, if one exists.

Parameters:

  • cname (String)

    The (unescaped) container name.

Returns:

  • (MockContainer, nil)

    The named MockContainer, or ‘nil` if none exist.



366
367
368
# File 'lib/fog/rackspace/storage.rb', line 366

def mock_container(cname)
  data[Fog::Rackspace.escape(cname)]
end

#mock_container!(cname) ⇒ MockContainer

Access a MockContainer with the specified name, raising a NotFound exception if none exist.

Parameters:

  • cname (String)

    The (unescaped) container name.

Returns:



377
378
379
# File 'lib/fog/rackspace/storage.rb', line 377

def mock_container!(cname)
  mock_container(cname) or raise Fog::Storage::Rackspace::NotFound.new
end

#not_found(path) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/fog/rackspace/requests/storage/delete_static_large_object.rb', line 63

def not_found(path)
  response = Excon::Response.new
  response.status = 200
  response.body = {
    "Number Not Found" => 1,
    "Response Status" => "200 OK",
    "Response Body" => "",
    "Errors" => [[path, "404 Not Found"]],
    "Number Deleted" => 0
  }
  response
end

#post_set_meta_temp_url_key(key) ⇒ Object



36
37
38
39
40
41
42
# File 'lib/fog/rackspace/requests/storage/post_set_meta_temp_url_key.rb', line 36

def post_set_meta_temp_url_key(key)
  ['X-Account-Meta-Temp-Url-Key'] = key

  response = Excon::Response.new
  response.status = 204
  response
end

#put_container(name, options = {}) ⇒ Object



24
25
26
27
28
29
30
31
32
33
34
# File 'lib/fog/rackspace/requests/storage/put_container.rb', line 24

def put_container(name, options={})
  existed = ! mock_container(name).nil?
  container = add_container(name)
  options.keys.each do |k|
    container.meta[k] = options[k].to_s if k =~ /^X-Container-Meta/
  end

  response = Excon::Response.new
  response.status = existed ? 202 : 201
  response
end

#put_dynamic_obj_manifest(container, object, options = {}) ⇒ Object



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/fog/rackspace/requests/storage/put_dynamic_obj_manifest.rb', line 42

def put_dynamic_obj_manifest(container, object, options = {})
  path = "#{Fog::Rackspace.escape(container)}/#{Fog::Rackspace.escape(object)}"

  # Escape the X-Object-Manifest header to match.
  explicit_manifest = options['X-Object-Manifest']
  if explicit_manifest
    parts = explicit_manifest.split('/', 2)
    explicit_manifest = parts.map { |p| Fog::Rackspace.escape p }.join('/')
  end

  c = mock_container! container
  o = c.add_object object, ''
  o.meta['X-Object-Manifest'] = explicit_manifest || path

  response = Excon::Response.new
  response.status = 201
  response
end

#put_object(container, object, data, options = {}, &block) ⇒ Object



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
# File 'lib/fog/rackspace/requests/storage/put_object.rb', line 45

def put_object(container, object, data, options = {}, &block)
  c = mock_container! container

  if block_given?
    data = ""
    loop do
      chunk = yield
      break if chunk.empty?
      data << chunk
    end
  end

  o = c.add_object object, data
  options.keys.each do |k|
    o.meta[k] = options[k].to_s if k =~ /^X-Object-Meta/
    o.meta[k] = options[k] if HeaderOptions.include? k
  end

  # Validate the provided Etag
  etag = o.meta['Etag']
  if etag && etag != o.hash
    c.remove_object object
    raise Fog::Storage::Rackspace::ServiceError.new
  end

  response = Excon::Response.new
  response.status = 201
  response
end

#put_static_obj_manifest(container, object, segments, options = {}) ⇒ Object



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
# File 'lib/fog/rackspace/requests/storage/put_static_obj_manifest.rb', line 56

def put_static_obj_manifest(container, object, segments, options = {})
  c = mock_container! container

  # Verify paths.
  errors = []
  segments.each do |segment|
    cname, oname = segment[:path].split('/', 2)
    target_container = mock_container(cname)

    raise Fog::Storage::Rackspace::NotFound.new unless target_container

    target_object = target_container.mock_object oname
    unless target_object
      errors << [segment[:path], '404 Not Found']
      next
    end

    unless target_object.hash == segment[:etag]
      errors << [segment[:path], 'Etag Mismatch']
    end

    unless target_object.bytes_used == segment[:size_bytes]
      errors << [segment[:path], 'Size Mismatch']
    end
  end

  unless errors.empty?
    response = Excon::Response.new
    response.status = 400
    response.body = Fog::JSON.encode({ 'Errors' => errors })

    error = Excon::Errors.status_error({}, response)
    raise Fog::Storage::Rackspace::BadRequest.slurp(error)
  end

  data = Fog::JSON.encode(segments)
  o = c.add_object object, data
  o.static_manifest = true

  response = Excon::Response.new
  response.status = 201
  response
end

#remove_container(cname) ⇒ Object

Remove a MockContainer with the specified name. No-op if the container does not exist.

Parameters:

  • cname (String)

    The (unescaped) container name.



394
395
396
# File 'lib/fog/rackspace/storage.rb', line 394

def remove_container(cname)
  data.delete Fog::Rackspace.escape(cname)
end

#reset_dataObject



357
358
359
# File 'lib/fog/rackspace/storage.rb', line 357

def reset_data
  self.class.data.delete(@rackspace_username)
end

#ssl?Boolean

Returns:

  • (Boolean)


398
399
400
# File 'lib/fog/rackspace/storage.rb', line 398

def ssl?
  !!@rackspace_cdn_ssl
end