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.



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

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.



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

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



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

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

.resetObject



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

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

Instance Method Details

#account_metaObject



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

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:



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

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

#dataObject



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

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

#delete_container(name) ⇒ Object



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

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



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

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



27
28
29
30
31
32
33
34
35
# File 'lib/fog/rackspace/requests/storage/delete_object.rb', line 27

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



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

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



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

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



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

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



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

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



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

def head_container(container)
  c = mock_container! container

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

#head_containersObject



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

def head_containers
  bytes_used = data.values.map { |c| c.bytes_used }.inject(0) { |a, b| a + b }
  container_count = data.size
  object_count = data.values.map { |c| c.objects.size }.inject(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



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

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.



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

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

#mock_container!(cname) ⇒ MockContainer

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

Parameters:

  • cname (String)

    The (unescaped) container name.

Returns:



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

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

#not_found(path) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/fog/rackspace/requests/storage/delete_static_large_object.rb', line 66

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



39
40
41
42
43
44
45
# File 'lib/fog/rackspace/requests/storage/post_set_meta_temp_url_key.rb', line 39

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



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

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



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

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



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

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



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

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.



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

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

#reset_dataObject



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

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

#ssl?Boolean

Returns:

  • (Boolean)


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

def ssl?
  !!@rackspace_cdn_ssl
end