oraclecloud 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +12 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +202 -0
- data/README.md +208 -0
- data/Rakefile +6 -0
- data/lib/oraclecloud.rb +36 -0
- data/lib/oraclecloud/asset.rb +65 -0
- data/lib/oraclecloud/assets.rb +99 -0
- data/lib/oraclecloud/client.rb +250 -0
- data/lib/oraclecloud/exceptions.rb +36 -0
- data/lib/oraclecloud/imagelist.rb +34 -0
- data/lib/oraclecloud/imagelists.rb +45 -0
- data/lib/oraclecloud/instance.rb +79 -0
- data/lib/oraclecloud/instance_request.rb +78 -0
- data/lib/oraclecloud/instances.rb +39 -0
- data/lib/oraclecloud/ip_association.rb +36 -0
- data/lib/oraclecloud/ip_associations.rb +29 -0
- data/lib/oraclecloud/orchestration.rb +85 -0
- data/lib/oraclecloud/orchestrations.rb +61 -0
- data/lib/oraclecloud/shape.rb +42 -0
- data/lib/oraclecloud/shapes.rb +36 -0
- data/lib/oraclecloud/sshkey.rb +32 -0
- data/lib/oraclecloud/sshkeys.rb +25 -0
- data/lib/oraclecloud/version.rb +20 -0
- data/oraclecloud.gemspec +26 -0
- metadata +156 -0
data/Rakefile
ADDED
data/lib/oraclecloud.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'oraclecloud/asset'
|
20
|
+
require 'oraclecloud/assets'
|
21
|
+
require 'oraclecloud/client'
|
22
|
+
require 'oraclecloud/exceptions'
|
23
|
+
require 'oraclecloud/imagelist'
|
24
|
+
require 'oraclecloud/imagelists'
|
25
|
+
require 'oraclecloud/instance'
|
26
|
+
require 'oraclecloud/instances'
|
27
|
+
require 'oraclecloud/instance_request'
|
28
|
+
require 'oraclecloud/ip_association'
|
29
|
+
require 'oraclecloud/ip_associations'
|
30
|
+
require 'oraclecloud/orchestration'
|
31
|
+
require 'oraclecloud/orchestrations'
|
32
|
+
require 'oraclecloud/shape'
|
33
|
+
require 'oraclecloud/shapes'
|
34
|
+
require 'oraclecloud/sshkey'
|
35
|
+
require 'oraclecloud/sshkeys'
|
36
|
+
require 'oraclecloud/version'
|
@@ -0,0 +1,65 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module OracleCloud
|
19
|
+
class Asset
|
20
|
+
attr_reader :asset_data, :asset_type, :client, :container, :path
|
21
|
+
|
22
|
+
def initialize(client, path)
|
23
|
+
@client = client
|
24
|
+
@asset_data = nil
|
25
|
+
@path = path
|
26
|
+
@container = path.split('/').first
|
27
|
+
|
28
|
+
local_init
|
29
|
+
validate!
|
30
|
+
|
31
|
+
fetch
|
32
|
+
end
|
33
|
+
|
34
|
+
def local_init
|
35
|
+
# this should be redefined in each Assets subclass with things like
|
36
|
+
# the @asset_type to use in API calls
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate!
|
40
|
+
raise "#{self.class} did not define an asset_type variable" if asset_type.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def fetch
|
44
|
+
@asset_data = client.single_item(asset_type, path)
|
45
|
+
end
|
46
|
+
alias_method :refresh, :fetch
|
47
|
+
|
48
|
+
def id
|
49
|
+
asset_data['name'].split('/').last
|
50
|
+
end
|
51
|
+
alias_method :name, :id
|
52
|
+
|
53
|
+
def name_with_container
|
54
|
+
"#{container}/#{id}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def full_name
|
58
|
+
"/Compute-#{client.identity_domain}/#{name_with_container}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def strip_identity_domain(name)
|
62
|
+
name.gsub("/Compute-#{client.identity_domain}/", '')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
module OracleCloud
|
19
|
+
class Assets
|
20
|
+
attr_reader :asset_klass, :asset_type, :client
|
21
|
+
attr_accessor :create_opts
|
22
|
+
|
23
|
+
def initialize(client)
|
24
|
+
@client = client
|
25
|
+
@create_opts = {}
|
26
|
+
|
27
|
+
local_init
|
28
|
+
validate!
|
29
|
+
end
|
30
|
+
|
31
|
+
def local_init
|
32
|
+
# this should be redefined in each Assets subclass with things like
|
33
|
+
# the @asset_type to use in API calls
|
34
|
+
end
|
35
|
+
|
36
|
+
def validate!
|
37
|
+
raise "#{self.class} did not define an asset_type variable" if asset_type.nil?
|
38
|
+
raise "#{self.class} did not define an asset_klass variable" if asset_klass.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def all
|
42
|
+
all_asset_ids_by_container.each_with_object([]) do |(container, asset_names), memo|
|
43
|
+
asset_names.each do |asset_name|
|
44
|
+
memo << @asset_klass.new(client, "#{container}/#{asset_name}")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def containers
|
50
|
+
directory('')
|
51
|
+
end
|
52
|
+
|
53
|
+
def ids_from_results(results)
|
54
|
+
results['result'].map { |x| x.split('/').last }
|
55
|
+
end
|
56
|
+
|
57
|
+
def all_asset_ids_by_container
|
58
|
+
containers.each_with_object({}) do |container, memo|
|
59
|
+
memo[container] = asset_ids_for_container(container)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def asset_ids_for_container(container)
|
64
|
+
directory(container)
|
65
|
+
end
|
66
|
+
|
67
|
+
def by_name(name)
|
68
|
+
@asset_klass.new(client, strip_identity_domain(name))
|
69
|
+
end
|
70
|
+
|
71
|
+
def directory(path)
|
72
|
+
ids_from_results(client.directory(asset_type, path))
|
73
|
+
end
|
74
|
+
|
75
|
+
def create(opts)
|
76
|
+
@create_opts = opts
|
77
|
+
|
78
|
+
validate_create_options!
|
79
|
+
response = client.http_post("/#{asset_type}/", create_request_payload.to_json)
|
80
|
+
name = strip_identity_domain(response['name'])
|
81
|
+
@asset_klass.new(client, name)
|
82
|
+
end
|
83
|
+
|
84
|
+
def create_request_payload
|
85
|
+
# this should be defined in each Assets subclass with a formatted
|
86
|
+
# payload used to create the Asset
|
87
|
+
raise NoMethodError, "#{self.class} does not define create_request_payload"
|
88
|
+
end
|
89
|
+
|
90
|
+
def validate_create_options!
|
91
|
+
# this should be redefined in each Assets subclass with any validation
|
92
|
+
# of creation options that should be done prior to creation
|
93
|
+
end
|
94
|
+
|
95
|
+
def strip_identity_domain(name)
|
96
|
+
name.gsub("/Compute-#{client.identity_domain}/", '')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
#
|
2
|
+
# Author:: Chef Partner Engineering (<partnereng@chef.io>)
|
3
|
+
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
|
4
|
+
# License:: Apache License, Version 2.0
|
5
|
+
#
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
7
|
+
# you may not use this file except in compliance with the License.
|
8
|
+
# You may obtain a copy of the License at
|
9
|
+
#
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
11
|
+
#
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
15
|
+
# See the License for the specific language governing permissions and
|
16
|
+
# limitations under the License.
|
17
|
+
#
|
18
|
+
|
19
|
+
require 'ffi_yajl'
|
20
|
+
require 'rest-client'
|
21
|
+
|
22
|
+
module OracleCloud
|
23
|
+
class Client # rubocop:disable Metrics/ClassLength
|
24
|
+
attr_reader :identity_domain, :password, :username
|
25
|
+
|
26
|
+
def initialize(opts)
|
27
|
+
@api_url = opts[:api_url]
|
28
|
+
@identity_domain = opts[:identity_domain]
|
29
|
+
@username = opts[:username]
|
30
|
+
@password = opts[:password]
|
31
|
+
@verify_ssl = opts.fetch(:verify_ssl, true)
|
32
|
+
@cookie = nil
|
33
|
+
|
34
|
+
validate_client_options!
|
35
|
+
end
|
36
|
+
|
37
|
+
#################################
|
38
|
+
#
|
39
|
+
# methods to other API objects
|
40
|
+
#
|
41
|
+
|
42
|
+
def imagelists
|
43
|
+
OracleCloud::ImageLists.new(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def instance_request(*args)
|
47
|
+
OracleCloud::InstanceRequest.new(self, *args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def instances
|
51
|
+
OracleCloud::Instances.new(self)
|
52
|
+
end
|
53
|
+
|
54
|
+
def ip_associations
|
55
|
+
OracleCloud::IPAssociations.new(self)
|
56
|
+
end
|
57
|
+
|
58
|
+
def orchestrations
|
59
|
+
OracleCloud::Orchestrations.new(self)
|
60
|
+
end
|
61
|
+
|
62
|
+
def shapes
|
63
|
+
OracleCloud::Shapes.new(self)
|
64
|
+
end
|
65
|
+
|
66
|
+
def sshkeys
|
67
|
+
OracleCloud::SSHKeys.new(self)
|
68
|
+
end
|
69
|
+
|
70
|
+
#################################
|
71
|
+
#
|
72
|
+
# client methods
|
73
|
+
#
|
74
|
+
|
75
|
+
def validate_client_options!
|
76
|
+
raise ArgumentError, 'Username, password and identity_domain are required' if
|
77
|
+
@username.nil? || @password.nil? || @identity_domain.nil?
|
78
|
+
raise ArgumentError, 'An API URL is required' if @api_url.nil?
|
79
|
+
raise ArgumentError, "API URL #{@api_url} is not a valid URI." unless valid_uri?(@api_url)
|
80
|
+
end
|
81
|
+
|
82
|
+
def valid_uri?(uri)
|
83
|
+
uri = URI.parse(uri)
|
84
|
+
uri.is_a?(URI::HTTP)
|
85
|
+
rescue URI::InvalidURIError
|
86
|
+
false
|
87
|
+
end
|
88
|
+
|
89
|
+
def username_with_domain
|
90
|
+
"#{compute_identity_domain}/#{@username}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def compute_identity_domain
|
94
|
+
"Compute-#{@identity_domain}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def authenticate!
|
98
|
+
path = '/authenticate/'
|
99
|
+
response = RestClient::Request.execute(method: :post,
|
100
|
+
url: full_url(path),
|
101
|
+
headers: request_headers,
|
102
|
+
payload: authenticate_payload.to_json,
|
103
|
+
verify_ssl: @verify_ssl)
|
104
|
+
|
105
|
+
rescue => e
|
106
|
+
raise_http_exception(e, path)
|
107
|
+
else
|
108
|
+
@cookie = process_auth_cookies(response.headers[:set_cookie])
|
109
|
+
end
|
110
|
+
|
111
|
+
def authenticated?
|
112
|
+
! @cookie.nil?
|
113
|
+
end
|
114
|
+
|
115
|
+
def request_headers(opts = {})
|
116
|
+
headers = { 'Content-Type' => 'application/oracle-compute-v3+json' }
|
117
|
+
|
118
|
+
if opts[:type] == :directory
|
119
|
+
headers['Accept'] = 'application/oracle-compute-v3+directory+json'
|
120
|
+
else
|
121
|
+
headers['Accept'] = 'application/oracle-compute-v3+json'
|
122
|
+
end
|
123
|
+
|
124
|
+
headers['Cookie'] = @cookie if @cookie
|
125
|
+
headers
|
126
|
+
end
|
127
|
+
|
128
|
+
def authenticate_payload
|
129
|
+
{
|
130
|
+
'user' => username_with_domain,
|
131
|
+
'password' => @password
|
132
|
+
}
|
133
|
+
end
|
134
|
+
|
135
|
+
def full_url(path)
|
136
|
+
@api_url + path
|
137
|
+
end
|
138
|
+
|
139
|
+
def process_auth_cookies(cookies)
|
140
|
+
cookie = cookies.find { |c| c.start_with?('nimbula=') }
|
141
|
+
raise 'No nimbula auth cookie received in authentication request' if cookie.nil?
|
142
|
+
|
143
|
+
cookie.gsub!(/ Path=.* Max-Age=.*$/, '')
|
144
|
+
cookie
|
145
|
+
end
|
146
|
+
|
147
|
+
def asset_get(request_type, asset_type, path)
|
148
|
+
url = url_with_identity_domain(asset_type, path)
|
149
|
+
http_get(request_type, url)
|
150
|
+
end
|
151
|
+
|
152
|
+
def asset_put(asset_type, path, payload = nil)
|
153
|
+
url = url_with_identity_domain(asset_type, path)
|
154
|
+
http_put(url, payload)
|
155
|
+
end
|
156
|
+
|
157
|
+
def asset_delete(asset_type, path)
|
158
|
+
url = url_with_identity_domain(asset_type, path)
|
159
|
+
http_delete(url)
|
160
|
+
end
|
161
|
+
|
162
|
+
def single_item(asset_type, path)
|
163
|
+
asset_get(:single, asset_type, path)
|
164
|
+
end
|
165
|
+
|
166
|
+
def directory(asset_type, path)
|
167
|
+
asset_get(:directory, asset_type, path)
|
168
|
+
end
|
169
|
+
|
170
|
+
def url_with_identity_domain(type, path = '')
|
171
|
+
"/#{type}/#{compute_identity_domain}/#{path}"
|
172
|
+
end
|
173
|
+
|
174
|
+
def http_get(request_type, url)
|
175
|
+
authenticate! unless authenticated?
|
176
|
+
|
177
|
+
response = RestClient::Request.execute(method: :get,
|
178
|
+
url: full_url(url),
|
179
|
+
headers: request_headers(type: request_type),
|
180
|
+
verify_ssl: @verify_ssl)
|
181
|
+
rescue => e
|
182
|
+
raise_http_exception(e, url)
|
183
|
+
else
|
184
|
+
FFI_Yajl::Parser.parse(response)
|
185
|
+
end
|
186
|
+
|
187
|
+
def http_post(path, payload)
|
188
|
+
authenticate! unless authenticated?
|
189
|
+
response = RestClient::Request.execute(method: :post,
|
190
|
+
url: full_url(path),
|
191
|
+
headers: request_headers,
|
192
|
+
payload: payload,
|
193
|
+
verify_ssl: @verify_ssl)
|
194
|
+
rescue => e
|
195
|
+
raise_http_exception(e, path)
|
196
|
+
else
|
197
|
+
FFI_Yajl::Parser.parse(response)
|
198
|
+
end
|
199
|
+
|
200
|
+
def http_put(path, payload = nil)
|
201
|
+
authenticate! unless authenticated?
|
202
|
+
response = RestClient::Request.execute(method: :put,
|
203
|
+
url: full_url(path),
|
204
|
+
headers: request_headers,
|
205
|
+
payload: payload,
|
206
|
+
verify_ssl: @verify_ssl)
|
207
|
+
rescue => e
|
208
|
+
raise_http_exception(e, path)
|
209
|
+
else
|
210
|
+
FFI_Yajl::Parser.parse(response)
|
211
|
+
end
|
212
|
+
|
213
|
+
def http_delete(path)
|
214
|
+
authenticate! unless authenticated?
|
215
|
+
response = RestClient::Request.execute(method: :delete,
|
216
|
+
url: full_url(path),
|
217
|
+
headers: request_headers,
|
218
|
+
verify_ssl: @verify_ssl)
|
219
|
+
rescue => e
|
220
|
+
raise_http_exception(e, path)
|
221
|
+
else
|
222
|
+
FFI_Yajl::Parser.parse(response)
|
223
|
+
end
|
224
|
+
|
225
|
+
def raise_http_exception(caught_exception, path)
|
226
|
+
raise unless caught_exception.respond_to?(:http_code)
|
227
|
+
|
228
|
+
if caught_exception.http_code == 404
|
229
|
+
klass = OracleCloud::Exception::HTTPNotFound
|
230
|
+
else
|
231
|
+
klass = OracleCloud::Exception::HTTPError
|
232
|
+
end
|
233
|
+
|
234
|
+
begin
|
235
|
+
error_body = FFI_Yajl::Parser.parse(caught_exception.response)
|
236
|
+
rescue
|
237
|
+
error_body = { 'message' => caught_exception.response }
|
238
|
+
end
|
239
|
+
|
240
|
+
exception = klass.new(code: caught_exception.http_code,
|
241
|
+
body: caught_exception.response,
|
242
|
+
klass: caught_exception.class,
|
243
|
+
error: error_body['message'].to_s,
|
244
|
+
path: path)
|
245
|
+
|
246
|
+
message = exception.error.empty? ? caught_exception.message : exception.error
|
247
|
+
raise exception, message
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|