oraclecloud 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -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