fog 1.7.0 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
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.