delphix 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,68 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'securerandom'
21
+
22
+ module Delphix
23
+ module Utils
24
+ # Return the date and time in "HTTP-date" format as defined by RFC 7231.
25
+ #
26
+ # @return [Date,Time] in "HTTP-date" format
27
+ def utc_httpdate
28
+ Time.now.utc.httpdate
29
+ end
30
+
31
+ # Generates a uniq UUID, this is then used to identify this session.
32
+ #
33
+ # @return [String]
34
+ # Memoize a uniq UUID used to identify this session.
35
+ #
36
+ def request_id
37
+ @uuid ||= SecureRandom.uuid
38
+ end
39
+ end
40
+ end
41
+
42
+ class Hash
43
+ # Returns a new Hash, recursively downcasing and converting all
44
+ # keys to symbols.
45
+ #
46
+ # @return [Hash]
47
+ #
48
+ def recursively_normalize_keys
49
+ recursively_transform_keys { |key| key.downcase.to_sym rescue key }
50
+ end
51
+
52
+ private # P R O P R I E T À P R I V A T A divieto di accesso
53
+
54
+ # support methods for recursively transforming nested hashes and arrays
55
+ #
56
+ def _recursively_transform_keys_in_object(object, &block)
57
+ case object
58
+ when Hash
59
+ object.each_with_object({}) do |(key, val), result|
60
+ result[yield(key)] = _recursively_transform_keys_in_object(val, &block)
61
+ end
62
+ when Array
63
+ object.map { |e| _recursively_transform_keys_in_object(e, &block) }
64
+ else
65
+ object
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ # The version number of the Delphix Gem
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api public
25
+ module Delphix
26
+ # Contains information about this gem's version
27
+ module Version
28
+ MAJOR = 0
29
+ MINOR = 1
30
+ PATCH = 0
31
+
32
+ # Returns a version string by joining MAJOR, MINOR, and PATCH with '.'
33
+ #
34
+ # @example
35
+ # Version.string # => '1.0.1'
36
+ #
37
+ def self.string
38
+ [MAJOR, MINOR, PATCH].join('.')
39
+ end
40
+ end
41
+
42
+ VERSION = Delphix::Version.string
43
+ end
@@ -0,0 +1,164 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ module Delphix
21
+ class WebClient
22
+ def self.request(method, url, headers, body, timeout, &callback)
23
+ request = Delphix::WebRequest.new(method, url, headers, body)
24
+
25
+ if callback
26
+ Thread.new do
27
+ callback.call(internal_request(request, timeout))
28
+ end
29
+ else
30
+ internal_request(request, timeout)
31
+ end
32
+ end
33
+
34
+ def self.internal_request(request, timeout)
35
+ HTTP_HEADERS.each { |key, value| request.add_header(key, value) }
36
+ response = nil
37
+
38
+ begin
39
+ case request.method
40
+ when :get
41
+ response = RestClient::Request.execute(
42
+ method: :get,
43
+ url: request.url,
44
+ headers: request.headers,
45
+ timeout: timeout
46
+ )
47
+ when :post
48
+ response = RestClient::Request.execute(
49
+ method: :post,
50
+ url: request.url,
51
+ payload: request.body,
52
+ headers: request.headers,
53
+ timeout: timeout
54
+ )
55
+ when :delete
56
+ response = RestClient::Request.execute(
57
+ method: :delete,
58
+ url: request.url,
59
+ payload: request.body,
60
+ headers: request.headers,
61
+ timeout: timeout
62
+ )
63
+ end
64
+ rescue RestClient::RequestTimeout
65
+ raise 'Request Timeout'
66
+ rescue RestClient::Exception => e
67
+ response = e.response
68
+ end
69
+
70
+ Delphix::WebResponse.new(response)
71
+ end
72
+ end
73
+
74
+ # @param [String, Symbol] name
75
+ # A string or symbol used to identify the key portion of the HTTP headers.
76
+ # @param [String, Symbol, Array] value
77
+ # A string, symbol or array containing the values of the HTTP header.
78
+ #
79
+ # @return [Hash]
80
+ # The result of the key/value pair.
81
+ #
82
+ def self.default_header(name, value)
83
+ @@default_headers[name] = value
84
+ end
85
+
86
+ # @return [Hash]
87
+ # The HTTP headers sent with a POST/GET/DELETE.
88
+ #
89
+ def self.clear_default_headers
90
+ @@default_headers = {}
91
+ end
92
+
93
+ # @param [Integer] seconds
94
+ # Set the number of seconds to wait for an HTTP timeout.
95
+ #
96
+ # @return [undefined]
97
+ #
98
+ def self.timeout(seconds)
99
+ @@timeout = seconds
100
+ end
101
+
102
+ # Define the #get, #post, and #delete helper methods for sending HTTP
103
+ # requests to the Delphix engine. You shouldn't need to use these methods
104
+ # directly, but they can be useful for debugging.
105
+ #
106
+ # The following HTTP methods are supported by the Delphix Appliance:
107
+ #
108
+ # GET - Retrieve data from the server where complex input is not needed.
109
+ # All GET requests are guaranteed to be read-only, but not all
110
+ # read-only requests are required to use GET. Simple input
111
+ # (strings, number, boolean values) can be passed as query
112
+ # parameters.
113
+ # POST - Issue a read/write operation, or make a read-only call that
114
+ # requires complex input. The optional body of the call is
115
+ # expressed as JSON.
116
+ # DELETE - Delete an object on the system. For languages that don't provide
117
+ # a native wrapper for DELETE, or for delete operations with
118
+ # optional input, all delete operations can also be invoked as POST
119
+ # to the same URL with /delete appended to it.
120
+ #
121
+ # Each method returns a hash that responds to #code, #headers, #body and
122
+ # #raw_body obtained from parsing the JSON object in the response body.
123
+ #
124
+ # @param url [String<URL>] url the url of where to send the request
125
+ # @param [Hash{Symbol => String}] parameters key-value data of the HTTP
126
+ # API request
127
+ # @param [Block] block block to execute when the request returns
128
+ # @return [Fixnum, #code] the response code from Delphix engine
129
+ # @return [Hash, #headers] headers, beautified with symbols and underscores
130
+ # @return [Hash, #body] body parsed response body where applicable (JSON
131
+ # responses are parsed to Objects/Associative Arrays)
132
+ # @return [Hash, #raw_body] raw_body un-parsed response body
133
+ #
134
+ # @api semipublic
135
+ [:get, :post, :delete].each do |method|
136
+ define_singleton_method(method) do |url, parameters = {}, &callback|
137
+ WebClient.request(method.to_sym, url, @@default_headers,
138
+ parameters.to_json, @@timeout, &callback)
139
+ end
140
+ end
141
+
142
+ module InstanceMethods
143
+ private
144
+
145
+ # Returns the current api endpoint, if present.
146
+ #
147
+ # @return [nil, String] the current endpoint
148
+ def endpoint
149
+ nil
150
+ end
151
+ end
152
+
153
+ # @!classmethods
154
+ module ClassMethods
155
+ # When present, lets you specify the api for the given client.
156
+ #
157
+ # @param [String, nil] value the endpoint to use.
158
+ # @example Setting a string endpoint endpoint '/resources/json/delphix'
159
+ # @example Unsetting the string endpoint endpoint nil
160
+ def endpoint(value = nil)
161
+ define_method(:endpoint) { value }
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'base64'
21
+ require 'addressable/uri'
22
+
23
+ module Delphix
24
+ class WebRequest
25
+ include Delphix::Utils
26
+
27
+ # @!attribute [r] method
28
+ # The HTTP method used, 'GET', 'POST', or 'DELETE'.
29
+ # @return [String]
30
+ attr_reader :method
31
+ # @!attribute [r] url
32
+ # The API path/URL to POST, GET, DELETE from/to.
33
+ # @return [String]
34
+ attr_reader :url
35
+ # @!attribute [r] headers
36
+ # The HTTP headers, symbolized and underscored.
37
+ # @return [Hash]
38
+ attr_reader :headers
39
+ # @!attribute [r] body
40
+ # The parsed response body where applicable (JSON responses are parsed
41
+ # to Objects/Associative Arrays).
42
+ # @return [Hash]
43
+ attr_reader :body
44
+ # @!attribute [r] all
45
+ # The raw_body un-parsed response body.
46
+ # @return [JSON]
47
+ attr_reader :all
48
+
49
+ def add_header(name, value)
50
+ @headers[name] = value
51
+ end
52
+
53
+ def initialize(method, url, headers = {}, body = nil)
54
+ @method = method
55
+
56
+ if (method == :get)
57
+ if body.is_a?(Hash) && body.length > 0
58
+ if url.include? '?'
59
+ url += '&'
60
+ else
61
+ url += '?'
62
+ end
63
+
64
+ uri = Addressable::URI.new
65
+ uri.query_values = body
66
+ url += uri.query
67
+ end
68
+ else
69
+ @body = body
70
+ end
71
+
72
+ unless url =~ URI.regexp
73
+ raise 'Invalid URL: ' + url
74
+ end
75
+
76
+ @url = url.gsub(/\s+/, '%20')
77
+
78
+ @headers = {
79
+ 'Date' => utc_httpdate,
80
+ 'Request-ID' => request_id
81
+ }
82
+
83
+ headers.each_pair { |key, value| @headers[key.downcase] = value }
84
+
85
+ Delphix.last_request = {
86
+ headers: @headers,
87
+ method: @method,
88
+ url: @url
89
+ }
90
+ begin
91
+ Delphix.last_request[:body] = JSON.parse(@body) if body.length > 2
92
+ rescue Exception
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'json'
21
+
22
+ module Delphix
23
+ class WebResponse
24
+ include Net::HTTPHeader
25
+
26
+ # @!attribute [r] code
27
+ # The HTTP response code from Delphix engine.
28
+ # @return [#code]
29
+ attr_reader :code
30
+ # @!attribute [r] method
31
+ # The raw_body un-parsed response body from the Delphix engine.
32
+ # @return [#raw_body]
33
+ attr_reader :raw_body
34
+ # @!attribute [r] method
35
+ # The parsed response body where applicable (JSON responses are parsed
36
+ # to Objects/Associative Arrays).
37
+ # @return [#body]
38
+ attr_reader :body
39
+ # @!attribute [r] method
40
+ # The HTTP headers, symbolized and underscored.
41
+ # @return [#headers]
42
+ attr_reader :headers
43
+ # @!attribute [r] method
44
+ # The current session cookies.
45
+ # @return [#raw_body]
46
+ attr_reader :cookies
47
+
48
+ def initialize(response)
49
+ @code = response.code
50
+ @headers = response.headers
51
+ @raw_body = response
52
+ @body = @raw_body
53
+ @cookies = response.cookies
54
+
55
+ Delphix.last_response = {
56
+ code: response.code,
57
+ headers: response.headers,
58
+ body: JSON.parse(response.body),
59
+ cookies: response.cookies,
60
+ description: response.description
61
+ }.recursively_normalize_keys
62
+
63
+ begin
64
+ @body = JSON.parse(@raw_body)
65
+ rescue Exception
66
+ end
67
+ end
68
+
69
+ def ==(other)
70
+ @headers == other
71
+ end
72
+
73
+ def inspect
74
+ @headers.inspect
75
+ end
76
+
77
+ def method_missing(name, *args, &block)
78
+ if @headers.respond_to?(name)
79
+ @headers.send(name, *args, &block)
80
+ else
81
+ super
82
+ end
83
+ end
84
+
85
+ def respond_to?(method)
86
+ super || @headers.respond_to?(method)
87
+ end
88
+ end
89
+ end
data/lib/delphix.rb ADDED
@@ -0,0 +1,205 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ # License: Apache License, Version 2.0
5
+ # Copyright: (C) 2014-2015 Stefano Harding
6
+ #
7
+ # Licensed under the Apache License, Version 2.0 (the "License");
8
+ # you may not use this file except in compliance with the License.
9
+ # You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing, software
14
+ # distributed under the License is distributed on an "AS IS" BASIS,
15
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ # See the License for the specific language governing permissions and
17
+ # limitations under the License.
18
+ #
19
+
20
+ require 'rest-client'
21
+ require_relative 'delphix/utils'
22
+ require_relative 'delphix/web_client'
23
+ require_relative 'delphix/web_request'
24
+ require_relative 'delphix/web_response'
25
+ require_relative 'delphix/version'
26
+
27
+ # A library for supporting connections to the Delphix API. The module Delphix
28
+ # is based on the singleton pattern, which restricts instantiation of a class
29
+ # to only one instance that is globally available.
30
+ #
31
+ # @example
32
+ # To establish a session with the Delphix appliance and get a cookie for
33
+ # your effort you issue the following sequence:
34
+ #
35
+ # * Configure the Delphix client to use the specified version of the API, in
36
+ # hash format as `{ major: 1, micro: 0, minor: 0 }`, for example:
37
+ # Delphix.api_version = {
38
+ # type: 'APIVersion',
39
+ # major: { major: 1, micro: 0, minor: 0 }
40
+ # }
41
+ # * Specify the Delphix server to connect to.
42
+ # Delphix.server = 'delphix.example.com'
43
+ # * Specify the Delphix username to connect with.
44
+ # Delphix.api_user = 'delphix'
45
+ # * Specify the Delphix password to use.
46
+ # Delphix.api_passwd = 'delphix'
47
+ # * Now you have an established session, go crazy.
48
+ #
49
+ module Delphix
50
+ include Utils
51
+
52
+ API_ENDPOINT = '/resources/json/delphix'
53
+ HTTP_HEADERS = {
54
+ 'Accept' => 'application/json; charset=UTF-8',
55
+ 'Content-Type' => 'application/json; charset=UTF-8',
56
+ 'User-Agent' => 'Delphix-Ruby-Client/1.0.0'
57
+ }
58
+
59
+ # Default timeout value in seconds.
60
+ @@timeout = 10
61
+
62
+ # Default headers
63
+ @@default_headers = {}
64
+
65
+ class << self
66
+ # @!attribute [rw] last_request
67
+ # @return [Hash] retruns the last request
68
+ attr_accessor :last_request
69
+ # @!attribute [rw] last_response
70
+ # @return [Hash] retruns the last response
71
+ attr_accessor :last_response
72
+ # @!attribute [rw] session
73
+ # @return [Hash] retruns current session state
74
+ # @return [#code] the response code from Delphix engine
75
+ # @return [#headers] beautified with symbols and underscores
76
+ # @return [#body] parsed response body
77
+ # @return [#raw_body] un-parsed response body
78
+ attr_accessor :session
79
+ # @!attribute [rw] api_version
80
+ # @return [Hash] containing the major, minor and micro version numbers.
81
+ attr_accessor :api_version
82
+ # @!attribute [rw] server
83
+ # @return [String] Delphix server address
84
+ attr_accessor :server
85
+ # @!attribute [rw] api_user
86
+ # @return [String] username to authenticate with
87
+ attr_accessor :api_user
88
+ # @!attribute [rw] api_passwd
89
+ # @return [String] password for authentication
90
+ attr_accessor :api_passwd
91
+ # @!attribute [rw] verbose
92
+ # @return [Nothing] enables verbosity
93
+ attr_accessor :verbose
94
+ end
95
+
96
+ # Returns the API endpoint for a given resource namespace by combining the
97
+ # server address with the appropriate HTTP headers.
98
+ #
99
+ # @param resource [Resource] namespace
100
+ #
101
+ # @return [URL] return the URL for the API endpoint
102
+ #
103
+ # @api public
104
+ def self.api_url(resource = nil)
105
+ 'http://' + @server + resource
106
+ end
107
+
108
+ def self.session
109
+ Delphix.default_header(:cookies, cookies)
110
+ @session ||= login(@api_user, @api_passwd)
111
+ end
112
+
113
+ # Establish a session with the Delphix engine and return an identifier
114
+ # through browser cookies. This session will be reused in subsequent calls,
115
+ # the same session credentials and state are preserved without requiring a
116
+ # re-authentication call. Sessions do not persisit between incovations.
117
+ #
118
+ # @return [Hash] cookies
119
+ # containing the new session cookies
120
+ #
121
+ # @api public
122
+ def self.cookies
123
+ @resp ||= Delphix.post session_url,
124
+ type: 'APISession', version: @api_version
125
+ @resp.cookies
126
+ end
127
+
128
+ # Authenticates the session so that API calls can be made. Only supports basic
129
+ # password authentication.
130
+ #
131
+ # @param [String] user
132
+ # user name to authenticate with
133
+ # @param [String] passwd
134
+ # password to authenticate with
135
+ #
136
+ # @return [Fixnum, #code]
137
+ # the response code from Delphix engine
138
+ # @return [Hash, #headers]
139
+ # headers, beautified with symbols and underscores
140
+ # @return [Hash, #body] body
141
+ # parsed response body where applicable (JSON responses are parsed to
142
+ # Objects/Associative Arrays)
143
+ # @return [Hash, #raw_body] raw_body
144
+ # un-parsed response body
145
+ #
146
+ # @api public
147
+ def self.login(user = @api_user, passwd = @api_passwd)
148
+ Delphix.post login_url,
149
+ type: 'LoginRequest', username: user, password: passwd
150
+ end
151
+
152
+ # @!method session
153
+ # A helper method to return the URL for the resource by using the
154
+ # resource_url shorthand.
155
+ # @return [String] The API path to session.
156
+ #
157
+ # @!method login
158
+ # A helper method to return the URL for the resource by using the
159
+ # resource_url shorthand.
160
+ # @return [String] The API path to login.
161
+ #
162
+ # @!method environment
163
+ # A helper method to return the URL for the resource by using the
164
+ # resource_url shorthand.
165
+ # @return [String] The API path to environment.
166
+ #
167
+ # @!method alert
168
+ # A helper method to return the URL for the resource by using the
169
+ # resource_url shorthand.
170
+ # @return [String] The API path to alert.
171
+ #
172
+ # @!method database
173
+ # A helper method to return the URL for the resource by using the
174
+ # resource_url shorthand.
175
+ # @return [String] The API path to database.
176
+ #
177
+ # @!method source
178
+ # A helper method to return the URL for the resource by using the
179
+ # resource_url shorthand.
180
+ # @return [String] The API path to source.
181
+ #
182
+ [:session, :login, :environment, :alert, :database, :source].each do |name|
183
+ define_singleton_method(name.to_s + '_url') do
184
+ api_url('/resources/json/delphix/' + name.to_s)
185
+ end
186
+ end
187
+
188
+ # @return [String] object inspection
189
+ # @!visibility private
190
+ def inspect
191
+ instance_variables.inject([
192
+ "\n#<#{self.class}:0x#{object_id.to_s(16)}>",
193
+ "\tInstance variables:"
194
+ ]) do |result, item|
195
+ result << "\t\t#{item} = #{instance_variable_get(item)}"
196
+ result
197
+ end.join("\n")
198
+ end
199
+
200
+ # @return [String] string of instance
201
+ # @!visibility private
202
+ def to_s
203
+ "<#{self.class}:0x#{object_id.to_s(16)} session=#{@session}>"
204
+ end
205
+ end