fog 1.7.0 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (219) hide show
  1. data/.gitignore +2 -2
  2. data/README.md +9 -10
  3. data/Rakefile +15 -299
  4. data/changelog.txt +153 -0
  5. data/docs/about/contributing.markdown +1 -0
  6. data/docs/dns/index.markdown +1 -1
  7. data/docs/index.markdown +8 -3
  8. data/fog.gemspec +3 -3
  9. data/lib/fog.rb +0 -5
  10. data/lib/fog/aws/auto_scaling.rb +1 -1
  11. data/lib/fog/aws/cloud_watch.rb +1 -1
  12. data/lib/fog/aws/compute.rb +2 -1
  13. data/lib/fog/aws/dynamodb.rb +30 -64
  14. data/lib/fog/aws/elb.rb +1 -1
  15. data/lib/fog/aws/glacier.rb +1 -1
  16. data/lib/fog/aws/iam.rb +1 -0
  17. data/lib/fog/aws/models/auto_scaling/group.rb +1 -1
  18. data/lib/fog/aws/models/compute/server.rb +2 -0
  19. data/lib/fog/aws/models/compute/volume.rb +0 -1
  20. data/lib/fog/aws/models/iam/user.rb +1 -1
  21. data/lib/fog/aws/parsers/compute/describe_instances.rb +3 -1
  22. data/lib/fog/aws/parsers/compute/describe_reserved_instances_offerings.rb +1 -1
  23. data/lib/fog/aws/parsers/storage/delete_multiple_objects.rb +50 -0
  24. data/lib/fog/aws/rds.rb +2 -2
  25. data/lib/fog/aws/requests/compute/describe_availability_zones.rb +3 -0
  26. data/lib/fog/aws/requests/compute/describe_reserved_instances_offerings.rb +11 -8
  27. data/lib/fog/aws/requests/compute/modify_volume_attribute.rb +51 -0
  28. data/lib/fog/aws/requests/dns/change_resource_record_sets.rb +1 -0
  29. data/lib/fog/aws/requests/storage/delete_multiple_objects.rb +172 -0
  30. data/lib/fog/aws/signaturev4.rb +1 -1
  31. data/lib/fog/aws/sqs.rb +1 -1
  32. data/lib/fog/aws/storage.rb +2 -0
  33. data/lib/fog/bluebox/requests/compute/create_block.rb +1 -3
  34. data/lib/fog/brightbox/compute.rb +239 -84
  35. data/lib/fog/brightbox/models/compute/account.rb +9 -3
  36. data/lib/fog/brightbox/models/compute/server.rb +2 -1
  37. data/lib/fog/brightbox/models/compute/servers.rb +33 -1
  38. data/lib/fog/brightbox/oauth2.rb +164 -0
  39. data/lib/fog/brightbox/requests/compute/activate_console_server.rb +9 -2
  40. data/lib/fog/brightbox/requests/compute/add_listeners_load_balancer.rb +11 -2
  41. data/lib/fog/brightbox/requests/compute/add_nodes_load_balancer.rb +11 -2
  42. data/lib/fog/brightbox/requests/compute/add_servers_server_group.rb +9 -11
  43. data/lib/fog/brightbox/requests/compute/apply_to_firewall_policy.rb +11 -2
  44. data/lib/fog/brightbox/requests/compute/create_api_client.rb +11 -2
  45. data/lib/fog/brightbox/requests/compute/create_application.rb +11 -2
  46. data/lib/fog/brightbox/requests/compute/create_cloud_ip.rb +11 -1
  47. data/lib/fog/brightbox/requests/compute/create_firewall_policy.rb +13 -2
  48. data/lib/fog/brightbox/requests/compute/create_firewall_rule.rb +17 -2
  49. data/lib/fog/brightbox/requests/compute/create_image.rb +18 -2
  50. data/lib/fog/brightbox/requests/compute/create_load_balancer.rb +14 -2
  51. data/lib/fog/brightbox/requests/compute/create_server.rb +17 -2
  52. data/lib/fog/brightbox/requests/compute/create_server_group.rb +11 -2
  53. data/lib/fog/brightbox/requests/compute/destroy_api_client.rb +9 -2
  54. data/lib/fog/brightbox/requests/compute/destroy_application.rb +9 -2
  55. data/lib/fog/brightbox/requests/compute/destroy_cloud_ip.rb +9 -2
  56. data/lib/fog/brightbox/requests/compute/destroy_firewall_policy.rb +9 -2
  57. data/lib/fog/brightbox/requests/compute/destroy_firewall_rule.rb +9 -2
  58. data/lib/fog/brightbox/requests/compute/destroy_image.rb +9 -2
  59. data/lib/fog/brightbox/requests/compute/destroy_load_balancer.rb +9 -2
  60. data/lib/fog/brightbox/requests/compute/destroy_server.rb +9 -2
  61. data/lib/fog/brightbox/requests/compute/destroy_server_group.rb +9 -2
  62. data/lib/fog/brightbox/requests/compute/get_account.rb +7 -15
  63. data/lib/fog/brightbox/requests/compute/get_api_client.rb +9 -2
  64. data/lib/fog/brightbox/requests/compute/get_application.rb +9 -2
  65. data/lib/fog/brightbox/requests/compute/get_authenticated_user.rb +1 -3
  66. data/lib/fog/brightbox/requests/compute/get_cloud_ip.rb +9 -2
  67. data/lib/fog/brightbox/requests/compute/get_firewall_policy.rb +9 -2
  68. data/lib/fog/brightbox/requests/compute/get_firewall_rule.rb +9 -2
  69. data/lib/fog/brightbox/requests/compute/get_image.rb +9 -2
  70. data/lib/fog/brightbox/requests/compute/get_interface.rb +9 -2
  71. data/lib/fog/brightbox/requests/compute/get_load_balancer.rb +9 -2
  72. data/lib/fog/brightbox/requests/compute/get_scoped_account.rb +1 -5
  73. data/lib/fog/brightbox/requests/compute/get_server.rb +9 -2
  74. data/lib/fog/brightbox/requests/compute/get_server_group.rb +9 -2
  75. data/lib/fog/brightbox/requests/compute/get_server_type.rb +9 -2
  76. data/lib/fog/brightbox/requests/compute/get_user.rb +7 -15
  77. data/lib/fog/brightbox/requests/compute/get_zone.rb +9 -2
  78. data/lib/fog/brightbox/requests/compute/list_accounts.rb +6 -2
  79. data/lib/fog/brightbox/requests/compute/list_api_clients.rb +8 -2
  80. data/lib/fog/brightbox/requests/compute/list_applications.rb +8 -2
  81. data/lib/fog/brightbox/requests/compute/list_cloud_ips.rb +8 -2
  82. data/lib/fog/brightbox/requests/compute/list_firewall_policies.rb +8 -2
  83. data/lib/fog/brightbox/requests/compute/list_images.rb +8 -2
  84. data/lib/fog/brightbox/requests/compute/list_load_balancers.rb +8 -2
  85. data/lib/fog/brightbox/requests/compute/list_server_groups.rb +8 -2
  86. data/lib/fog/brightbox/requests/compute/list_server_types.rb +8 -2
  87. data/lib/fog/brightbox/requests/compute/list_servers.rb +8 -2
  88. data/lib/fog/brightbox/requests/compute/list_users.rb +8 -2
  89. data/lib/fog/brightbox/requests/compute/list_zones.rb +8 -2
  90. data/lib/fog/brightbox/requests/compute/map_cloud_ip.rb +11 -2
  91. data/lib/fog/brightbox/requests/compute/move_servers_server_group.rb +10 -12
  92. data/lib/fog/brightbox/requests/compute/remove_firewall_policy.rb +11 -2
  93. data/lib/fog/brightbox/requests/compute/remove_listeners_load_balancer.rb +11 -2
  94. data/lib/fog/brightbox/requests/compute/remove_nodes_load_balancer.rb +11 -2
  95. data/lib/fog/brightbox/requests/compute/remove_servers_server_group.rb +9 -10
  96. data/lib/fog/brightbox/requests/compute/reset_ftp_password_account.rb +22 -3
  97. data/lib/fog/brightbox/requests/compute/reset_ftp_password_scoped_account.rb +18 -0
  98. data/lib/fog/brightbox/requests/compute/reset_secret_api_client.rb +13 -1
  99. data/lib/fog/brightbox/requests/compute/reset_secret_application.rb +9 -2
  100. data/lib/fog/brightbox/requests/compute/shutdown_server.rb +9 -2
  101. data/lib/fog/brightbox/requests/compute/snapshot_server.rb +9 -2
  102. data/lib/fog/brightbox/requests/compute/start_server.rb +9 -2
  103. data/lib/fog/brightbox/requests/compute/stop_server.rb +9 -2
  104. data/lib/fog/brightbox/requests/compute/unmap_cloud_ip.rb +9 -2
  105. data/lib/fog/brightbox/requests/compute/update_account.rb +32 -34
  106. data/lib/fog/brightbox/requests/compute/update_api_client.rb +12 -2
  107. data/lib/fog/brightbox/requests/compute/update_application.rb +12 -2
  108. data/lib/fog/brightbox/requests/compute/update_cloud_ip.rb +13 -2
  109. data/lib/fog/brightbox/requests/compute/update_firewall_rule.rb +16 -1
  110. data/lib/fog/brightbox/requests/compute/update_image.rb +17 -2
  111. data/lib/fog/brightbox/requests/compute/update_load_balancer.rb +15 -2
  112. data/lib/fog/brightbox/requests/compute/update_scoped_account.rb +12 -19
  113. data/lib/fog/brightbox/requests/compute/update_server.rb +12 -2
  114. data/lib/fog/brightbox/requests/compute/update_server_group.rb +12 -2
  115. data/lib/fog/brightbox/requests/compute/update_user.rb +15 -2
  116. data/lib/fog/cloudstack/models/compute/server.rb +3 -1
  117. data/lib/fog/core.rb +1 -0
  118. data/lib/fog/core/connection.rb +1 -0
  119. data/lib/fog/google/storage.rb +13 -2
  120. data/lib/fog/libvirt/models/compute/server.rb +1 -0
  121. data/lib/fog/libvirt/requests/compute/list_domains.rb +2 -2
  122. data/lib/fog/openstack.rb +57 -58
  123. data/lib/fog/openstack/compute.rb +15 -14
  124. data/lib/fog/openstack/identity.rb +10 -2
  125. data/lib/fog/openstack/image.rb +1 -1
  126. data/lib/fog/openstack/models/compute/flavor.rb +5 -1
  127. data/lib/fog/openstack/models/compute/security_group.rb +1 -1
  128. data/lib/fog/openstack/models/compute/server.rb +5 -0
  129. data/lib/fog/openstack/models/identity/users.rb +1 -2
  130. data/lib/fog/openstack/requests/compute/create_flavor.rb +4 -1
  131. data/lib/fog/openstack/requests/compute/create_security_group.rb +1 -1
  132. data/lib/fog/openstack/requests/compute/get_limits.rb +93 -0
  133. data/lib/fog/openstack/requests/compute/list_tenants.rb +1 -0
  134. data/lib/fog/openstack/requests/compute/release_address.rb +13 -1
  135. data/lib/fog/openstack/requests/compute/reset_server_state.rb +24 -0
  136. data/lib/fog/openstack/requests/identity/create_role.rb +1 -1
  137. data/lib/fog/openstack/requests/identity/set_tenant.rb +21 -0
  138. data/lib/fog/openstack/volume.rb +2 -1
  139. data/lib/fog/rackspace/models/compute_v2/server.rb +27 -0
  140. data/lib/fog/rackspace/models/compute_v2/servers.rb +8 -0
  141. data/lib/fog/rackspace/models/dns/record.rb +14 -1
  142. data/lib/fog/rackspace/models/storage/file.rb +68 -2
  143. data/lib/fog/rackspace/requests/compute_v2/create_server.rb +3 -0
  144. data/lib/fog/version.rb +5 -0
  145. data/lib/fog/vsphere/compute.rb +74 -8
  146. data/lib/fog/vsphere/models/compute/cluster.rb +31 -0
  147. data/lib/fog/vsphere/models/compute/clusters.rb +26 -0
  148. data/lib/fog/vsphere/models/compute/datacenter.rb +35 -0
  149. data/lib/fog/vsphere/models/compute/datacenters.rb +23 -0
  150. data/lib/fog/vsphere/models/compute/datastore.rb +24 -0
  151. data/lib/fog/vsphere/models/compute/datastores.rb +25 -0
  152. data/lib/fog/vsphere/models/compute/folder.rb +28 -0
  153. data/lib/fog/vsphere/models/compute/folders.rb +27 -0
  154. data/lib/fog/vsphere/models/compute/interface.rb +39 -0
  155. data/lib/fog/vsphere/models/compute/interfaces.rb +33 -0
  156. data/lib/fog/vsphere/models/compute/network.rb +21 -0
  157. data/lib/fog/vsphere/models/compute/networks.rb +25 -0
  158. data/lib/fog/vsphere/models/compute/resource_pool.rb +23 -0
  159. data/lib/fog/vsphere/models/compute/resource_pools.rb +26 -0
  160. data/lib/fog/vsphere/models/compute/server.rb +78 -12
  161. data/lib/fog/vsphere/models/compute/servers.rb +16 -20
  162. data/lib/fog/vsphere/models/compute/template.rb +13 -0
  163. data/lib/fog/vsphere/models/compute/templates.rb +23 -0
  164. data/lib/fog/vsphere/models/compute/volume.rb +45 -0
  165. data/lib/fog/vsphere/models/compute/volumes.rb +33 -0
  166. data/lib/fog/vsphere/requests/compute/create_vm.rb +114 -0
  167. data/lib/fog/vsphere/requests/compute/get_cluster.rb +25 -0
  168. data/lib/fog/vsphere/requests/compute/get_datacenter.rb +29 -0
  169. data/lib/fog/vsphere/requests/compute/get_datastore.rb +25 -0
  170. data/lib/fog/vsphere/requests/compute/get_folder.rb +73 -0
  171. data/lib/fog/vsphere/requests/compute/get_network.rb +25 -0
  172. data/lib/fog/vsphere/requests/compute/get_resource_pool.rb +26 -0
  173. data/lib/fog/vsphere/requests/compute/get_virtual_machine.rb +62 -0
  174. data/lib/fog/vsphere/requests/compute/list_clusters.rb +37 -0
  175. data/lib/fog/vsphere/requests/compute/list_datacenters.rb +34 -0
  176. data/lib/fog/vsphere/requests/compute/list_datastores.rb +40 -0
  177. data/lib/fog/vsphere/requests/compute/list_folders.rb +45 -0
  178. data/lib/fog/vsphere/requests/compute/list_networks.rb +38 -0
  179. data/lib/fog/vsphere/requests/compute/list_resource_pools.rb +39 -0
  180. data/lib/fog/vsphere/requests/compute/list_virtual_machines.rb +132 -166
  181. data/lib/fog/vsphere/requests/compute/list_vm_interfaces.rb +52 -0
  182. data/lib/fog/vsphere/requests/compute/list_vm_volumes.rb +51 -0
  183. data/lib/fog/vsphere/requests/compute/vm_clone.rb +6 -8
  184. data/lib/fog/vsphere/requests/compute/vm_destroy.rb +1 -8
  185. data/lib/fog/vsphere/requests/compute/vm_reconfig_hardware.rb +1 -2
  186. data/lib/tasks/changelog_task.rb +98 -0
  187. data/lib/tasks/documentation_task.rb +155 -0
  188. data/lib/tasks/test_task.rb +46 -0
  189. data/tests/aws/models/iam/users_tests.rb +16 -2
  190. data/tests/aws/requests/auto_scaling/notification_configuration_tests.rb +1 -0
  191. data/tests/aws/requests/auto_scaling/tag_tests.rb +1 -0
  192. data/tests/aws/requests/compute/instance_tests.rb +2 -0
  193. data/tests/aws/requests/compute/volume_tests.rb +8 -0
  194. data/tests/aws/requests/storage/object_tests.rb +18 -1
  195. data/tests/aws/requests/storage/versioning_tests.rb +70 -0
  196. data/tests/brightbox/compute_tests.rb +96 -4
  197. data/tests/brightbox/models/compute/account_tests.rb +15 -0
  198. data/tests/brightbox/oauth2_tests.rb +103 -0
  199. data/tests/brightbox/requests/compute/account_tests.rb +9 -2
  200. data/tests/brightbox/requests/compute/interface_tests.rb +18 -4
  201. data/tests/dns/models/record_tests.rb +17 -3
  202. data/tests/openstack/requests/compute/address_tests.rb +22 -19
  203. data/tests/openstack/requests/compute/flavor_tests.rb +4 -2
  204. data/tests/openstack/requests/compute/limit_tests.rb +60 -0
  205. data/tests/openstack/requests/compute/quota_tests.rb +16 -3
  206. data/tests/openstack/requests/compute/security_group_tests.rb +1 -1
  207. data/tests/rackspace/models/compute_v2/servers_tests.rb +6 -0
  208. data/tests/rackspace/models/storage/file_tests.rb +172 -0
  209. data/tests/rackspace/requests/dns/helper.rb +12 -26
  210. data/tests/vsphere/compute_tests.rb +3 -3
  211. data/tests/vsphere/models/compute/server_tests.rb +1 -2
  212. data/tests/vsphere/requests/compute/list_virtual_machines_tests.rb +5 -13
  213. data/tests/vsphere/requests/compute/vm_clone_tests.rb +2 -2
  214. metadata +59 -11
  215. data/lib/fog/vsphere/requests/compute/datacenters.rb +0 -34
  216. data/lib/fog/vsphere/requests/compute/find_vm_by_ref.rb +0 -41
  217. data/lib/fog/vsphere/requests/compute/vm_create.rb +0 -97
  218. data/tests/vsphere/requests/compute/find_vm_by_ref_tests.rb +0 -26
  219. data/tests/vsphere/requests/compute/vm_create_tests.rb +0 -20
@@ -198,6 +198,7 @@ module Fog
198
198
  @elb_hosted_zone_mapping ||= {
199
199
  "ap-northeast-1" => "Z2YN17T5R711GT",
200
200
  "ap-southeast-1" => "Z1WI8VXHPB1R38",
201
+ "ap-southeast-2" => "Z2999QAZ9SRTIC",
201
202
  "eu-west-1" => "Z3NF1Z3NOM5OY2",
202
203
  "sa-east-1" => "Z2ES78Y61JGQKS",
203
204
  "us-east-1" => "Z3DZXE0Q79N41H",
@@ -0,0 +1,172 @@
1
+ module Fog
2
+ module Storage
3
+ class AWS
4
+ class Real
5
+
6
+ require 'fog/aws/parsers/storage/delete_multiple_objects'
7
+
8
+ # Delete multiple objects from S3
9
+ #
10
+ # ==== Parameters
11
+ # * bucket_name<~String> - Name of bucket containing object to delete
12
+ # * object_names<~Array> - Array of object names to delete
13
+ #
14
+ # ==== Returns
15
+ # * response<~Excon::Response>:
16
+ # * body<~Hash>:
17
+ # * 'DeleteResult'<~Array>:
18
+ # * 'Deleted'<~Hash>:
19
+ # * 'Key'<~String> - Name of the object that was deleted
20
+ # * 'VersionId'<~String> - ID for the versioned onject in case of a versioned delete
21
+ # * 'DeleteMarker'<~Boolean> - Indicates if the request accessed a delete marker
22
+ # * 'DeleteMarkerVersionId'<~String> - Version ID of the delete marker accessed
23
+ # * 'Error'<~Hash>:
24
+ # * 'Key'<~String> - Name of the object that failed to be deleted
25
+ # * 'VersionId'<~String> - ID of the versioned object that was attempted to be deleted
26
+ # * 'Code'<~String> - Status code for the result of the failed delete
27
+ # * 'Message'<~String> - Error description
28
+ #
29
+ # ==== See Also
30
+ # http://docs.amazonwebservices.com/AmazonS3/latest/API/multiobjectdeleteapi.html
31
+
32
+ # bucket_name -- name of the bucket to use
33
+ # object_names -- filename
34
+ # For versioned deletes, options should include a version_ids hash, which
35
+ # maps from filename to an array of versions.
36
+ # The semantics are that for each (object_name, version) tuple, the
37
+ # caller must insert the object_name and an associated version (if
38
+ # desired), so for n versions, the object must be inserted n times.
39
+ def delete_multiple_objects(bucket_name, object_names, options = {})
40
+ data = "<Delete>"
41
+ data << "<Quiet>true</Quiet>" if options.delete(:quiet)
42
+ version_ids = options.delete('versionId')
43
+ object_names.each do |object_name|
44
+ data << "<Object>"
45
+ data << "<Key>#{CGI.escape(object_name)}</Key>"
46
+ object_version = version_ids.nil? ? nil : version_ids[object_name]
47
+ if object_version
48
+ data << "<VersionId>#{CGI.escape(object_version)}</VersionId>"
49
+ end
50
+ data << "</Object>"
51
+ end
52
+ data << "</Delete>"
53
+
54
+ headers = options
55
+ headers['Content-Length'] = data.length
56
+ headers['Content-MD5'] = Base64.encode64(Digest::MD5.digest(data)).
57
+ gsub("\n", '')
58
+
59
+ request({
60
+ :body => data,
61
+ :expects => 200,
62
+ :headers => headers,
63
+ :host => "#{bucket_name}.#{@host}",
64
+ :method => 'POST',
65
+ :parser => Fog::Parsers::Storage::AWS::DeleteMultipleObjects.new,
66
+ :query => {'delete' => nil}
67
+ })
68
+ end
69
+
70
+ end
71
+
72
+ class Mock # :nodoc:all
73
+
74
+ def delete_multiple_objects(bucket_name, object_names, options = {})
75
+ response = Excon::Response.new
76
+ if bucket = self.data[:buckets][bucket_name]
77
+ response.status = 200
78
+ response.body = { 'DeleteResult' => [] }
79
+ version_ids = options.delete('versionId')
80
+ object_names.each do |object_name|
81
+ object_version = version_ids.nil? ? nil : version_ids[object_name]
82
+ response.body['DeleteResult'] << delete_object_helper(bucket,
83
+ object_name,
84
+ object_version)
85
+ end
86
+ else
87
+ response.status = 404
88
+ raise(Excon::Errors.status_error({:expects => 200}, response))
89
+ end
90
+ response
91
+ end
92
+
93
+ private
94
+
95
+ def delete_object_helper(bucket, object_name, version_id)
96
+ response = { 'Deleted' => {} }
97
+ if bucket[:versioning]
98
+ bucket[:objects][object_name] ||= []
99
+
100
+ if version_id
101
+ version = bucket[:objects][object_name].find { |object| object['VersionId'] == version_id}
102
+
103
+ # S3 special cases the 'null' value to not error out if no such version exists.
104
+ if version || (version_id == 'null')
105
+ bucket[:objects][object_name].delete(version)
106
+ bucket[:objects].delete(object_name) if bucket[:objects][object_name].empty?
107
+
108
+ response['Deleted'] = { 'Key' => object_name,
109
+ 'VersionId' => version_id,
110
+ 'DeleteMarker' => 'true',
111
+ 'DeleteMarkerVersionId' => version_id
112
+ }
113
+ else
114
+ response = delete_error_body(object_name,
115
+ version_id,
116
+ 'InvalidVersion',
117
+ 'Invalid version ID specified')
118
+ end
119
+ else
120
+ delete_marker = {
121
+ :delete_marker => true,
122
+ 'Key' => object_name,
123
+ 'VersionId' => bucket[:versioning] == 'Enabled' ? Fog::Mock.random_base64(32) : 'null',
124
+ 'Last-Modified' => Fog::Time.now.to_date_header
125
+ }
126
+
127
+ # When versioning is suspended, a delete marker is placed if the last object ID is not the value 'null',
128
+ # otherwise the last object is replaced.
129
+ if bucket[:versioning] == 'Suspended' && bucket[:objects][object_name].first['VersionId'] == 'null'
130
+ bucket[:objects][object_name].shift
131
+ end
132
+
133
+ bucket[:objects][object_name].unshift(delete_marker)
134
+
135
+ response['Deleted'] = { 'Key' => object_name,
136
+ 'VersionId' => delete_marker['VersionId'],
137
+ 'DeleteMarkerVersionId' =>
138
+ delete_marker['VersionId'],
139
+ 'DeleteMarker' => 'true',
140
+ }
141
+ end
142
+ else
143
+ if version_id && version_id != 'null'
144
+ response = delete_error_body(object_name,
145
+ version_id,
146
+ 'InvalidVersion',
147
+ 'Invalid version ID specified')
148
+ response = invalid_version_id_payload(version_id)
149
+ else
150
+ bucket[:objects].delete(object_name)
151
+ response['Deleted'] = { 'Key' => object_name }
152
+ end
153
+ end
154
+ response
155
+ end
156
+
157
+ def delete_error_body(key, version_id, message, code)
158
+ {
159
+ 'Error' => {
160
+ 'Code' => code,
161
+ 'Message' => message,
162
+ 'VersionId' => version_id,
163
+ 'Key' => key,
164
+ }
165
+ }
166
+ end
167
+
168
+ end
169
+ end
170
+ end
171
+ end
172
+
@@ -70,4 +70,4 @@ DATA
70
70
 
71
71
  end
72
72
  end
73
- end
73
+ end
@@ -40,7 +40,7 @@ module Fog
40
40
  setup_credentials(options)
41
41
  @region = options[:region] || 'us-east-1'
42
42
 
43
- unless ['ap-northeast-1', 'ap-southeast-1', 'eu-west-1', 'us-east-1', 'us-west-1', 'us-west-2', 'sa-east-1'].include?(@region)
43
+ unless ['ap-northeast-1', 'ap-southeast-1', 'ap-southeast-2', 'eu-west-1', 'us-east-1', 'us-west-1', 'us-west-2', 'sa-east-1'].include?(@region)
44
44
  raise ArgumentError, "Unknown region: #{@region.inspect}"
45
45
  end
46
46
  end
@@ -26,6 +26,7 @@ module Fog
26
26
  request :delete_bucket_policy
27
27
  request :delete_bucket_website
28
28
  request :delete_object
29
+ request :delete_multiple_objects
29
30
  request :get_bucket
30
31
  request :get_bucket_acl
31
32
  request :get_bucket_lifecycle
@@ -337,6 +338,7 @@ DATA
337
338
  for key in (params[:query] || {}).keys.sort
338
339
  if %w{
339
340
  acl
341
+ delete
340
342
  lifecycle
341
343
  location
342
344
  logging
@@ -30,14 +30,12 @@ module Fog
30
30
  'location' => location_id
31
31
  }
32
32
 
33
- body = URI.encode options.map {|k,v| "#{k}=#{v}"}.join('&')
34
-
35
33
  request(
36
34
  :expects => 200,
37
35
  :method => 'POST',
38
36
  :path => '/api/blocks.json',
39
37
  :query => query,
40
- :body => URI.encode(body)
38
+ :body => options.map {|k,v| "#{CGI.escape(k)}=#{CGI.escape(v)}"}.join('&')
41
39
  )
42
40
  end
43
41
 
@@ -1,5 +1,6 @@
1
1
  require 'fog/brightbox'
2
2
  require 'fog/compute'
3
+ require 'fog/brightbox/oauth2'
3
4
 
4
5
  module Fog
5
6
  module Compute
@@ -16,6 +17,12 @@ module Fog
16
17
  # User credentials (still requires client details)
17
18
  recognizes :brightbox_username, :brightbox_password, :brightbox_account
18
19
 
20
+ # Cached tokens
21
+ recognizes :brightbox_access_token, :brightbox_refresh_token
22
+
23
+ # Automatic token management
24
+ recognizes :brightbox_token_management
25
+
19
26
  # Excon connection settings
20
27
  recognizes :persistent
21
28
 
@@ -106,6 +113,7 @@ module Fog
106
113
  request :remove_nodes_load_balancer
107
114
  request :remove_servers_server_group
108
115
  request :reset_ftp_password_account
116
+ request :reset_ftp_password_scoped_account
109
117
  request :reset_secret_api_client
110
118
  request :reset_secret_application
111
119
  request :shutdown_server
@@ -126,6 +134,139 @@ module Fog
126
134
  request :update_user
127
135
 
128
136
  module Shared
137
+ include Fog::Brightbox::OAuth2
138
+
139
+ # Creates a new instance of the Brightbox Compute service
140
+ #
141
+ # @note If you open a connection using just a refresh token when it
142
+ # expires the service will no longer be able to authenticate.
143
+ #
144
+ # @param [Hash] options
145
+ # @option options [String] :brightbox_api_url Override the default (or configured) API endpoint
146
+ # @option options [String] :brightbox_auth_url Override the default (or configured) API authentication endpoint
147
+ # @option options [String] :brightbox_client_id Client identifier to authenticate with (overrides configured)
148
+ # @option options [String] :brightbox_secret Client secret to authenticate with (overrides configured)
149
+ # @option options [String] :brightbox_username Email or user identifier for user based authentication
150
+ # @option options [String] :brightbox_password Password for user based authentication
151
+ # @option options [String] :brightbox_account Account identifier to scope this connection to
152
+ # @option options [String] :connection_options Settings to pass to underlying {Fog::Connection}
153
+ # @option options [Boolean] :persistent Sets a persistent HTTP {Fog::Connection}
154
+ # @option options [String] :brightbox_access_token Sets the OAuth access token to use rather than requesting a new token
155
+ # @option options [String] :brightbox_refresh_token Sets the refresh token to use when requesting a newer access token
156
+ # @option options [String] :brightbox_token_management Overide the existing behaviour to request access tokens if expired (default is `true`)
157
+ #
158
+ def initialize(options)
159
+ # Currently authentication and api endpoints are the same but may change
160
+ @auth_url = options[:brightbox_auth_url] || Fog.credentials[:brightbox_auth_url] || API_URL
161
+ @auth_connection = Fog::Connection.new(@auth_url)
162
+
163
+ @api_url = options[:brightbox_api_url] || Fog.credentials[:brightbox_api_url] || API_URL
164
+ @connection_options = options[:connection_options] || {}
165
+ @persistent = options[:persistent] || false
166
+ @connection = Fog::Connection.new(@api_url, @persistent, @connection_options)
167
+
168
+ # Authentication options
169
+ client_id = options[:brightbox_client_id] || Fog.credentials[:brightbox_client_id]
170
+ client_secret = options[:brightbox_secret] || Fog.credentials[:brightbox_secret]
171
+
172
+ username = options[:brightbox_username] || Fog.credentials[:brightbox_username]
173
+ password = options[:brightbox_password] || Fog.credentials[:brightbox_password]
174
+ @configured_account = options[:brightbox_account] || Fog.credentials[:brightbox_account]
175
+ # Request account can be changed at anytime and changes behaviour of future requests
176
+ @scoped_account = @configured_account
177
+
178
+ credential_options = {:username => username, :password => password}
179
+ @credentials = CredentialSet.new(client_id, client_secret, credential_options)
180
+
181
+ # If existing tokens have been cached, allow continued use of them in the service
182
+ @credentials.update_tokens(options[:brightbox_access_token], options[:brightbox_refresh_token])
183
+
184
+ @token_management = options.fetch(:brightbox_token_management, true)
185
+ end
186
+
187
+ # Sets the scoped account for future requests
188
+ # @param [String] scoped_account Identifier of the account to scope request to
189
+ def scoped_account=(scoped_account)
190
+ @scoped_account = scoped_account
191
+ end
192
+
193
+ # This returns the account identifier that the request should be scoped by
194
+ # based on the options passed to the request and current configuration
195
+ #
196
+ # @param [String] options_account Any identifier passed into the request
197
+ #
198
+ # @return [String, nil] The account identifier to scope the request to or nil
199
+ def scoped_account(options_account = nil)
200
+ [options_account, @scoped_account].compact.first
201
+ end
202
+
203
+ # Resets the scoped account back to intially configured one
204
+ def scoped_account_reset
205
+ @scoped_account = @configured_account
206
+ end
207
+
208
+ # Returns the scoped account being used for requests
209
+ #
210
+ # * For API clients this is the owning account
211
+ # * For User applications this is the account specified by either +account_id+
212
+ # option on a connection or the +brightbox_account+ setting in your configuration
213
+ #
214
+ # @return [Fog::Compute::Brightbox::Account]
215
+ #
216
+ def account
217
+ Fog::Compute::Brightbox::Account.new(get_scoped_account).tap do |acc|
218
+ # Connection is more like the compute 'service'
219
+ acc.connection = self
220
+ end
221
+ end
222
+
223
+ # Returns true if authentication is being performed as a user
224
+ # @return [Boolean]
225
+ def authenticating_as_user?
226
+ @credentials.user_details?
227
+ end
228
+
229
+ # Returns true if an access token is set
230
+ # @return [Boolean]
231
+ def access_token_available?
232
+ !! @credentials.access_token
233
+ end
234
+
235
+ # Returns the current access token or nil
236
+ # @return [String,nil]
237
+ def access_token
238
+ @credentials.access_token
239
+ end
240
+
241
+ # Returns the current refresh token or nil
242
+ # @return [String,nil]
243
+ def refresh_token
244
+ @credentials.refresh_token
245
+ end
246
+
247
+ # Requests a new access token
248
+ #
249
+ # @return [String] New access token
250
+ def get_access_token
251
+ begin
252
+ get_access_token!
253
+ rescue Excon::Errors::Unauthorized, Excon::Errors::BadRequest
254
+ @credentials.update_tokens(nil, nil)
255
+ end
256
+ @credentials.access_token
257
+ end
258
+
259
+ # Requests a new access token and raises if there is a problem
260
+ #
261
+ # @return [String] New access token
262
+ # @raise [Excon::Errors::BadRequest] The credentials are expired or incorrect
263
+ #
264
+ def get_access_token!
265
+ response = request_access_token(@auth_connection, @credentials)
266
+ update_credentials_from_response(@credentials, response)
267
+ @credentials.access_token
268
+ end
269
+
129
270
  # Returns an identifier for the default image for use
130
271
  #
131
272
  # Currently tries to find the latest version Ubuntu LTS (i686) widening
@@ -139,119 +280,133 @@ module Fog
139
280
  return @default_image_id unless @default_image_id.nil?
140
281
  @default_image_id = Fog.credentials[:brightbox_default_image] || select_default_image
141
282
  end
283
+
284
+ private
285
+
286
+ # This makes a request of the API based on the configured setting for
287
+ # token management.
288
+ #
289
+ # @param [Hash] options Excon compatible options
290
+ # @see https://github.com/geemus/excon/blob/master/lib/excon/connection.rb
291
+ #
292
+ # @return [Hash] Data of response body
293
+ #
294
+ def make_request(options)
295
+ if @token_management
296
+ managed_token_request(options)
297
+ else
298
+ authenticated_request(options)
299
+ end
300
+ end
301
+
302
+ # This request checks for access tokens and will ask for a new one if
303
+ # it receives Unauthorized from the API before repeating the request
304
+ #
305
+ # @param [Hash] options Excon compatible options
306
+ #
307
+ # @return [Excon::Response]
308
+ def managed_token_request(options)
309
+ begin
310
+ get_access_token unless access_token_available?
311
+ response = authenticated_request(options)
312
+ rescue Excon::Errors::Unauthorized
313
+ get_access_token
314
+ response = authenticated_request(options)
315
+ end
316
+ end
317
+
318
+ # This request makes an authenticated request of the API using currently
319
+ # setup credentials.
320
+ #
321
+ # @param [Hash] options Excon compatible options
322
+ #
323
+ # @return [Excon::Response]
324
+ def authenticated_request(options)
325
+ headers = options[:headers] || {}
326
+ headers.merge!("Authorization" => "OAuth #{@credentials.access_token}", "Content-Type" => "application/json")
327
+ options[:headers] = headers
328
+ # TODO This is just a wrapper around a call to Excon::Connection#request
329
+ # so can be extracted from Compute by passing in the connection,
330
+ # credentials and options
331
+ @connection.request(options)
332
+ end
142
333
  end
143
334
 
335
+ # The Mock Service allows you to run a fake instance of the Service
336
+ # which makes no real connections.
337
+ #
338
+ # @todo Implement
339
+ #
144
340
  class Mock
145
341
  include Shared
146
342
 
147
- def initialize(options)
148
- @brightbox_client_id = options[:brightbox_client_id] || Fog.credentials[:brightbox_client_id]
149
- @brightbox_secret = options[:brightbox_secret] || Fog.credentials[:brightbox_secret]
343
+ def request(method, path, expected_responses, parameters = {})
344
+ _request
150
345
  end
151
346
 
152
- def request(options)
153
- raise "Not implemented"
347
+ def request_access_token(connection, credentials)
348
+ _request
154
349
  end
155
350
 
156
351
  private
352
+
353
+ def _request
354
+ raise Fog::Errors::MockNotImplemented
355
+ end
356
+
157
357
  def select_default_image
158
358
  "img-mockd"
159
359
  end
160
360
  end
161
361
 
362
+ # The Real Service actually makes real connections to the Brightbox
363
+ # service.
364
+ #
162
365
  class Real
163
366
  include Shared
164
367
 
165
- def initialize(options)
166
- # Currently authentication and api endpoints are the same but may change
167
- @auth_url = options[:brightbox_auth_url] || Fog.credentials[:brightbox_auth_url] || API_URL
168
- @api_url = options[:brightbox_api_url] || Fog.credentials[:brightbox_api_url] || API_URL
169
- @connection_options = options[:connection_options] || {}
170
- @brightbox_client_id = options[:brightbox_client_id] || Fog.credentials[:brightbox_client_id]
171
- @brightbox_secret = options[:brightbox_secret] || Fog.credentials[:brightbox_secret]
172
- @brightbox_username = options[:brightbox_username] || Fog.credentials[:brightbox_username]
173
- @brightbox_password = options[:brightbox_password] || Fog.credentials[:brightbox_password]
174
- @brightbox_account = options[:brightbox_account] || Fog.credentials[:brightbox_account]
175
- @persistent = options[:persistent] || false
176
- @connection = Fog::Connection.new(@api_url, @persistent, @connection_options)
177
- end
178
-
179
- def request(method, url, expected_responses, options = {})
368
+ # Makes an API request to the given path using passed options or those
369
+ # set with the service setup
370
+ #
371
+ # @todo Standard Fog behaviour is to return the Excon::Response but
372
+ # this was unintentionally changed to be the Hash version of the
373
+ # data in the body. This loses access to some details and should
374
+ # be corrected in a backwards compatible manner
375
+ #
376
+ # @param [String] method HTTP method to use for the request
377
+ # @param [String] path The absolute path for the request
378
+ # @param [Array<Fixnum>] expected_responses HTTP response codes that have been successful
379
+ # @param [Hash] parameters Keys and values for JSON
380
+ # @option parameters [String] :account_id The scoping account if required
381
+ #
382
+ # @return [Hash]
383
+ def request(method, path, expected_responses, parameters = {})
180
384
  request_options = {
181
385
  :method => method.to_s.upcase,
182
- :path => url,
386
+ :path => path,
183
387
  :expects => expected_responses
184
388
  }
185
- options[:account_id] = @brightbox_account if options[:account_id].nil? && @brightbox_account
186
- request_options[:body] = Fog::JSON.encode(options) unless options.empty?
187
- make_request(request_options)
188
- end
189
-
190
- # Returns the scoped account being used for requests
191
- #
192
- # API Clients:: This is the owning account
193
- # User Apps:: This is the account specified by either +account_id+
194
- # option on a connection or the +brightbox_account+
195
- # setting in your configuration
196
- #
197
- # === Returns:
198
- #
199
- # <tt>Fog::Compute::Brightbox::Account</tt>
200
- #
201
- def account
202
- Fog::Compute::Brightbox::Account.new(get_scoped_account)
203
- end
204
389
 
205
- private
206
- def get_oauth_token(options = {})
207
- auth_url = options[:brightbox_auth_url] || @auth_url
208
-
209
- connection = Fog::Connection.new(auth_url)
210
- authentication_body_hash = if @brightbox_username && @brightbox_password
211
- {
212
- 'client_id' => @brightbox_client_id,
213
- 'grant_type' => 'password',
214
- 'username' => @brightbox_username,
215
- 'password' => @brightbox_password
216
- }
217
- else
218
- {'client_id' => @brightbox_client_id, 'grant_type' => 'none'}
390
+ # Select the account to scope for this request
391
+ account = scoped_account(parameters.fetch(:account_id, nil))
392
+ if account
393
+ request_options[:query] = { :account_id => account }
219
394
  end
220
- @authentication_body = Fog::JSON.encode(authentication_body_hash)
221
-
222
- response = connection.request({
223
- :path => "/token",
224
- :expects => 200,
225
- :headers => {
226
- 'Authorization' => "Basic " + Base64.encode64("#{@brightbox_client_id}:#{@brightbox_secret}").chomp,
227
- 'Content-Type' => 'application/json'
228
- },
229
- :method => 'POST',
230
- :body => @authentication_body
231
- })
232
- @oauth_token = Fog::JSON.decode(response.body)["access_token"]
233
- return @oauth_token
234
- end
235
395
 
236
- def make_request(params)
237
- begin
238
- get_oauth_token if @oauth_token.nil?
239
- response = authenticated_request(params)
240
- rescue Excon::Errors::Unauthorized
241
- get_oauth_token
242
- response = authenticated_request(params)
243
- end
396
+ request_options[:body] = Fog::JSON.encode(parameters) unless parameters.empty?
397
+
398
+ response = make_request(request_options)
399
+
400
+ # FIXME We should revert to returning the Excon::Request after a suitable
401
+ # configuration option is in place to switch back to this incorrect behaviour
244
402
  unless response.body.empty?
245
- response = Fog::JSON.decode(response.body)
403
+ Fog::JSON.decode(response.body)
404
+ else
405
+ response
246
406
  end
247
407
  end
248
408
 
249
- def authenticated_request(options)
250
- headers = options[:headers] || {}
251
- headers.merge!("Authorization" => "OAuth #{@oauth_token}", "Content-Type" => "application/json")
252
- options[:headers] = headers
253
- @connection.request(options)
254
- end
409
+ private
255
410
 
256
411
  # Queries the API and tries to select the most suitable official Image
257
412
  # to use if the user chooses not to select their own.