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.



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

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.



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

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



326
327
328
329
330
# File 'lib/fog/rackspace/storage.rb', line 326

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

.resetObject



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

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

Instance Method Details

#account_metaObject



359
360
361
# File 'lib/fog/rackspace/storage.rb', line 359

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:



392
393
394
# File 'lib/fog/rackspace/storage.rb', line 392

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

#dataObject



355
356
357
# File 'lib/fog/rackspace/storage.rb', line 355

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.



372
373
374
# File 'lib/fog/rackspace/storage.rb', line 372

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:



383
384
385
# File 'lib/fog/rackspace/storage.rb', line 383

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.



400
401
402
# File 'lib/fog/rackspace/storage.rb', line 400

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

#reset_dataObject



363
364
365
# File 'lib/fog/rackspace/storage.rb', line 363

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

#ssl?Boolean

Returns:

  • (Boolean)


404
405
406
# File 'lib/fog/rackspace/storage.rb', line 404

def ssl?
  !!@rackspace_cdn_ssl
end