croesus 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +121 -0
  3. data/.ruby-version +1 -0
  4. data/API_operation.txt +197 -0
  5. data/CHANGELOG.md +0 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE.txt +0 -0
  8. data/README.md +146 -0
  9. data/Rakefile +26 -0
  10. data/bin/console_cmd.rb +133 -0
  11. data/croesus.gemspec +39 -0
  12. data/lib/croesus/associations.rb +46 -0
  13. data/lib/croesus/attribute.rb +41 -0
  14. data/lib/croesus/attributes.rb +93 -0
  15. data/lib/croesus/coerce.rb +110 -0
  16. data/lib/croesus/coercions/boolean_definitions.rb +32 -0
  17. data/lib/croesus/coercions/date_definitions.rb +30 -0
  18. data/lib/croesus/coercions/date_time_definitions.rb +30 -0
  19. data/lib/croesus/coercions/fixnum_definitions.rb +32 -0
  20. data/lib/croesus/coercions/float_definitions.rb +30 -0
  21. data/lib/croesus/coercions/hash_definitions.rb +27 -0
  22. data/lib/croesus/coercions/integer_definitions.rb +29 -0
  23. data/lib/croesus/coercions/string_definitions.rb +43 -0
  24. data/lib/croesus/coercions/time_definitions.rb +30 -0
  25. data/lib/croesus/core_ext/blank.rb +123 -0
  26. data/lib/croesus/core_ext/hash.rb +185 -0
  27. data/lib/croesus/dsl/dsl.rb +35 -0
  28. data/lib/croesus/dsl/helpers.rb +43 -0
  29. data/lib/croesus/dsl/mod_factory.rb +191 -0
  30. data/lib/croesus/dsl/resource_dsl.rb +59 -0
  31. data/lib/croesus/dsl/route_dsl.rb +42 -0
  32. data/lib/croesus/identity_map.rb +98 -0
  33. data/lib/croesus/platform.rb +47 -0
  34. data/lib/croesus/querying.rb +63 -0
  35. data/lib/croesus/resources/about.rb +15 -0
  36. data/lib/croesus/resources/basic_methods.rb +36 -0
  37. data/lib/croesus/resources/connectivity.rb +42 -0
  38. data/lib/croesus/resources/container.rb +135 -0
  39. data/lib/croesus/resources/fault.rb +38 -0
  40. data/lib/croesus/resources/fault_effect.rb +24 -0
  41. data/lib/croesus/resources/group.rb +37 -0
  42. data/lib/croesus/resources/host.rb +26 -0
  43. data/lib/croesus/resources/job.rb +27 -0
  44. data/lib/croesus/resources/namespace.rb +39 -0
  45. data/lib/croesus/resources/policy.rb +38 -0
  46. data/lib/croesus/resources/source.rb +86 -0
  47. data/lib/croesus/resources/source_config.rb +54 -0
  48. data/lib/croesus/resources/source_environment.rb +58 -0
  49. data/lib/croesus/resources/source_repository.rb +14 -0
  50. data/lib/croesus/resources/system_info.rb +21 -0
  51. data/lib/croesus/resources/timeflow.rb +40 -0
  52. data/lib/croesus/resources/timeflow_snapshot.rb +38 -0
  53. data/lib/croesus/utils.rb +262 -0
  54. data/lib/croesus/validations/many.rb +27 -0
  55. data/lib/croesus/validations/optional.rb +27 -0
  56. data/lib/croesus/validations.rb +91 -0
  57. data/lib/croesus/validators/base.rb +47 -0
  58. data/lib/croesus/validators/boolean_validator.rb +32 -0
  59. data/lib/croesus/validators/email_validator.rb +36 -0
  60. data/lib/croesus/validators/enumerable_validator.rb +40 -0
  61. data/lib/croesus/validators/hash_validator.rb +50 -0
  62. data/lib/croesus/validators/lambda_validator.rb +54 -0
  63. data/lib/croesus/validators/many_validator.rb +57 -0
  64. data/lib/croesus/validators/optional_validator.rb +41 -0
  65. data/lib/croesus/validators/presence_validator.rb +36 -0
  66. data/lib/croesus/validators/simple_type_validators.rb +38 -0
  67. data/lib/croesus/validators/simple_validator.rb +40 -0
  68. data/lib/croesus/version.rb +43 -0
  69. data/lib/croesus/web_client/web_client.rb +153 -0
  70. data/lib/croesus/web_client/web_request.rb +77 -0
  71. data/lib/croesus/web_client/web_response.rb +70 -0
  72. data/lib/croesus.rb +250 -0
  73. metadata +325 -0
@@ -0,0 +1,153 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 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
16
+ # implied. See the License for the specific language governing
17
+ # permissions and limitations under the License.
18
+ #
19
+
20
+ module Croesus
21
+ class WebClient
22
+ def self.request(method, url, headers, body, timeout, &callback)
23
+ request = Croesus::WebRequest.new(method, url, headers, body)
24
+
25
+ if callback
26
+ Thread.new do
27
+ callback.call(self.internal_request(request, timeout))
28
+ end
29
+ else
30
+ self.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
+ Croesus::WebResponse.new(response)
71
+ end
72
+ end
73
+
74
+ def self.default_header(name, value)
75
+ @@default_headers[name] = value
76
+ end
77
+
78
+ def self.clear_default_headers
79
+ @@default_headers = {}
80
+ end
81
+
82
+ def self.timeout(seconds)
83
+ @@timeout = seconds
84
+ end
85
+
86
+ # Define the #get, #post, and #delete helper methods for sending HTTP
87
+ # requests to the Delphix engine. You shouldn't need to use these methods
88
+ # directly, but they can be useful for debugging.
89
+ #
90
+ # The following HTTP methods are supported by the Delphix Appliance:
91
+ #
92
+ # GET - Retrieve data from the server where complex input is not needed.
93
+ # All GET requests are guaranteed to be read-only, but not all
94
+ # read-only requests are required to use GET. Simple input
95
+ # (strings, number, boolean values) can be passed as query
96
+ # parameters.
97
+ # POST - Issue a read/write operation, or make a read-only call that
98
+ # requires complex input. The optional body of the call is
99
+ # expressed as JSON.
100
+ # DELETE - Delete an object on the system. For languages that don't provide
101
+ # a native wrapper for DELETE, or for delete operations with
102
+ # optional input, all delete operations can also be invoked as POST
103
+ # to the same URL with /delete appended to it.
104
+ #
105
+ # Each method returns a hash that responds to #code, #headers, #body and
106
+ # #raw_body obtained from parsing the JSON object in the response body.
107
+ #
108
+ # @param url [String<URL>] url the url of where to send the request
109
+ # @param [Hash{Symbol => String}] parameters key-value data of the HTTP
110
+ # API request
111
+ # @param [Block] block block to execute when the request returns
112
+ # @return [Fixnum, #code] the response code from Delphix engine
113
+ # @return [Hash, #headers] headers, beautified with symbols and underscores
114
+ # @return [Hash, #body] body parsed response body where applicable (JSON
115
+ # responses are parsed to Objects/Associative Arrays)
116
+ # @return [Hash, #raw_body] raw_body un-parsed response body
117
+ #
118
+ # @api semipublic
119
+ [:get, :post, :delete].each do |method|
120
+ define_singleton_method(method) do |url, parameters = {}, &callback|
121
+ WebClient.request(method.to_sym, url, @@default_headers,
122
+ parameters.to_json, @@timeout, &callback)
123
+ end
124
+ end
125
+
126
+ # The most important part of the client functionality - namely, provides a
127
+ # set of tools and methods that make it possible to build RESTfully CRUD
128
+ # messages to and from the Delphix Engine.
129
+ module InstanceMethods
130
+
131
+ private # P R O P R I E T À P R I V A T A Vietato L'accesso
132
+
133
+ # Returns the current api endpoint, if present.
134
+ #
135
+ # @return [nil, String] the current endpoint
136
+ def endpoint
137
+ nil
138
+ end
139
+ end
140
+
141
+ # Class level methods to let you configure your api client.
142
+ module ClassMethods
143
+
144
+ # When present, lets you specify the api for the given client.
145
+ #
146
+ # @param [String, nil] value the endpoint to use.
147
+ # @example Setting a string endpoint endpoint '/resources/json/delphix'
148
+ # @example Unsetting the string endpoint endpoint nil
149
+ def endpoint(value = nil)
150
+ define_method(:endpoint) { value }
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,77 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 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
16
+ # implied. See the License for the specific language governing
17
+ # permissions and limitations under the License.
18
+ #
19
+
20
+ require 'addressable/uri' unless defined?(Addressable)
21
+ require 'base64' unless defined?(Base64)
22
+
23
+ module Croesus
24
+ class WebRequest
25
+ extend Croesus::Utils::ClassMethods
26
+ include Croesus::Utils
27
+
28
+ attr_reader :method, :url, :headers, :body, :all
29
+
30
+ def add_header(name, value)
31
+ @headers[name] = value
32
+ end
33
+
34
+ def initialize(method, url, headers = {}, body = nil)
35
+ @method = method
36
+
37
+ if (method == :get)
38
+ if body.is_a?(Hash) && body.length > 0
39
+ if url.include? "?"
40
+ url += "&"
41
+ else
42
+ url += "?"
43
+ end
44
+
45
+ uri = Addressable::URI.new
46
+ uri.query_values = body
47
+ url += uri.query
48
+ end
49
+ else
50
+ @body = body
51
+ end
52
+
53
+ unless url =~ URI.regexp
54
+ raise "Invalid URL: " + url
55
+ end
56
+
57
+ @url = url.gsub(/\s+/, '%20')
58
+
59
+ @headers = {
60
+ 'Date' => utc_httpdate,
61
+ 'Request-ID' => request_id,
62
+ }
63
+
64
+ headers.each_pair {|key, value| @headers[key.downcase] = value }
65
+
66
+ Croesus.last_request = {
67
+ headers: @headers,
68
+ method: @method,
69
+ url: @url
70
+ }
71
+ begin
72
+ Croesus.last_request[:body] = JSON.parse(@body) if body.length > 2
73
+ rescue Exception
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 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
16
+ # implied. See the License for the specific language governing
17
+ # permissions and limitations under the License.
18
+ #
19
+
20
+ require 'json'
21
+
22
+ module Croesus
23
+ class WebResponse
24
+ include Croesus::Utils
25
+ include ::Net::HTTPHeader
26
+
27
+ attr_reader :code, :raw_body, :body, :headers, :cookies
28
+
29
+ def initialize(response)
30
+ @code = response.code;
31
+ @headers = response.headers
32
+ @raw_body = response
33
+ @body = @raw_body
34
+ @cookies = response.cookies
35
+
36
+ Croesus.last_response = {
37
+ code: response.code,
38
+ headers: response.headers,
39
+ body: JSON.parse(response.body),
40
+ cookies: response.cookies,
41
+ description: response.description
42
+ }.recursively_normalize_keys
43
+
44
+ begin
45
+ @body = JSON.parse(@raw_body)
46
+ rescue Exception
47
+ end
48
+ end
49
+
50
+ def ==(other)
51
+ @headers == other
52
+ end
53
+
54
+ def inspect
55
+ @headers.inspect
56
+ end
57
+
58
+ def method_missing(name, *args, &block)
59
+ if @headers.respond_to?(name)
60
+ @headers.send(name, *args, &block)
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def respond_to?(method)
67
+ super || @headers.respond_to?(method)
68
+ end
69
+ end
70
+ end
data/lib/croesus.rb ADDED
@@ -0,0 +1,250 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Author: Stefano Harding <riddopic@gmail.com>
4
+ #
5
+ # Copyright (C) 2014 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
16
+ # implied. See the License for the specific language governing
17
+ # permissions and limitations under the License.
18
+ #
19
+
20
+ # Debuging hook 'n' stow
21
+ require 'ap'
22
+
23
+ # Croesus Ruby bindings
24
+ require 'rest-client'
25
+ require 'command_line_reporter'
26
+
27
+ # Core Ruby Extentions
28
+ require 'croesus/core_ext/blank'
29
+ require 'croesus/core_ext/hash'
30
+
31
+ # Version
32
+ require 'croesus/version'
33
+
34
+ # Base
35
+ require 'croesus/utils'
36
+ require 'croesus/platform'
37
+ require 'croesus/querying'
38
+ require 'croesus/attribute'
39
+ require 'croesus/attributes'
40
+ require 'croesus/validations'
41
+ require 'croesus/identity_map'
42
+
43
+ # DSL
44
+ require 'croesus/dsl/dsl'
45
+ require 'croesus/dsl/helpers'
46
+ require 'croesus/dsl/mod_factory'
47
+ require 'croesus/dsl/resource_dsl'
48
+ require 'croesus/dsl/route_dsl'
49
+
50
+ # Resources
51
+ require 'croesus/resources/basic_methods'
52
+ require 'croesus/resources/about'
53
+ require 'croesus/resources/connectivity'
54
+ require 'croesus/resources/container'
55
+ require 'croesus/resources/fault'
56
+ require 'croesus/resources/fault_effect'
57
+ require 'croesus/resources/group'
58
+ require 'croesus/resources/host'
59
+ require 'croesus/resources/job'
60
+ require 'croesus/resources/namespace'
61
+ require 'croesus/resources/policy'
62
+ require 'croesus/resources/source'
63
+ require 'croesus/resources/source_config'
64
+ require 'croesus/resources/source_environment'
65
+ require 'croesus/resources/source_repository'
66
+ require 'croesus/resources/system_info'
67
+ require 'croesus/resources/timeflow'
68
+ require 'croesus/resources/timeflow_snapshot'
69
+
70
+ # Web CLient
71
+ require 'croesus/web_client/web_client'
72
+ require 'croesus/web_client/web_request'
73
+ require 'croesus/web_client/web_response'
74
+
75
+ # A library for supporting connections to the Delphix API.
76
+ #
77
+ module Croesus
78
+ extend Utils::ClassMethods
79
+ include IdentityMap
80
+ include Querying
81
+ include Utils
82
+
83
+ NotFound = Class.new StandardError
84
+ APIError = Class.new StandardError
85
+ CoercionError = Class.new StandardError
86
+ ValidationError = Class.new StandardError
87
+ ConfigurationError = Class.new StandardError
88
+ InvalidStateError = Class.new StandardError
89
+ InvalidMethodError = Class.new StandardError
90
+ InvalidArgumentCount = Class.new StandardError
91
+
92
+ API_VERSION = { type: 'APIVersion', major: 1, minor: 3, micro: 0 }
93
+
94
+ API_ENDPOINT = '/resources/json/delphix'
95
+
96
+ HTTP_HEADERS = {
97
+ 'Accept' => 'application/json; charset=UTF-8',
98
+ 'Content-Type' => 'application/json; charset=UTF-8',
99
+ 'User-Agent' => "Croesus/#{Croesus::VERSION} " \
100
+ "(#{RUBY_ENGINE}/#{RUBY_PLATFORM} " \
101
+ "#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL})"
102
+ }
103
+
104
+ @@timeout = 10
105
+ @@default_headers = {}
106
+
107
+ # Represents an undefined parameter used by auto-generated option methods
108
+ Undefined = Object.new.freeze
109
+
110
+ class << self
111
+ # @!attribute [rw] last_request
112
+ # @return [Hash] retruns the last request
113
+ attr_accessor :last_request
114
+
115
+ # @!attribute [rw] last_response
116
+ # @return [Hash] retruns the last response
117
+ attr_accessor :last_response
118
+
119
+ # @!attribute [rw] session
120
+ # @return [Hash] retruns current session state
121
+ # @return [#code] the response code from Delphix engine
122
+ # @return [#headers] beautified with symbols and underscores
123
+ # @return [#body] parsed response body
124
+ # @return [#raw_body] un-parsed response body
125
+ attr_accessor :session
126
+
127
+ # @!attribute [rw] server
128
+ # @return [String] Delphix server address
129
+ attr_accessor :server
130
+
131
+ # @!attribute [rw] api_user
132
+ # @return [String] username to authenticate with
133
+ attr_accessor :api_user
134
+
135
+ # @!attribute [rw] api_passwd
136
+ # @return [String] password for authentication
137
+ attr_accessor :api_passwd
138
+
139
+ # @!attribute [rw] verbose
140
+ # @return [Nothing] enables verbosity
141
+ attr_accessor :verbose
142
+ end
143
+
144
+ @inheritance ||= 'not implemented'
145
+ Croesus::IdentityMap.enabled = true
146
+
147
+ # Returns the API endpoint for a given resource namespace by combining the
148
+ # server address with the appropriate HTTP headers.
149
+ #
150
+ # @param resource [Resource] namespace
151
+ #
152
+ # @return [URL] return the URL for the API endpoint
153
+ #
154
+ # @api public
155
+ def self.api_url(resource = nil)
156
+ 'http://' + @server + resource
157
+ end
158
+
159
+ def self.session
160
+ Croesus.default_header(:cookies, cookies)
161
+ @session ||= login(@api_user, @api_passwd)
162
+ end
163
+
164
+ # Establish a session with the Delphix engine and return an identifier
165
+ # through browser cookies. This session will be reused in subsequent calls,
166
+ # the same session credentials and state are preserved without requiring a
167
+ # re-authentication call. Sessions do not persisit between incovations.
168
+ #
169
+ # @return [Hash] cookies
170
+ # containing the new session cookies
171
+ #
172
+ # @api public
173
+ def self.cookies
174
+ @resp ||= Croesus.post session_url,
175
+ { type: 'APISession', version: API_VERSION }
176
+ @resp.cookies
177
+ end
178
+
179
+ # Authenticates the session so that API calls can be made. Only supports basic
180
+ # password authentication.
181
+ #
182
+ # @param [String] user
183
+ # user name to authenticate with
184
+ # @param [String] passwd
185
+ # password to authenticate with
186
+ #
187
+ # @return [Fixnum, #code]
188
+ # the response code from Delphix engine
189
+ # @return [Hash, #headers]
190
+ # headers, beautified with symbols and underscores
191
+ # @return [Hash, #body] body
192
+ # parsed response body where applicable (JSON responses are parsed to
193
+ # Objects/Associative Arrays)
194
+ # @return [Hash, #raw_body] raw_body
195
+ # un-parsed response body
196
+ #
197
+ # @api public
198
+ def self.login(user = @api_user, passwd = @api_passwd)
199
+ Croesus.post login_url,
200
+ { type: 'LoginRequest', username: user, password: passwd }
201
+ end
202
+
203
+ # Provides a wraper around getting the URL for the resource by using the
204
+ # resource_url shorthand.
205
+ #
206
+ # @return [URL] return the API URL for the given resource.
207
+ #
208
+ # @api public
209
+ [:session, :login, :environment, :alert, :database, :source].each do |name|
210
+ define_singleton_method(name.to_s + '_url') do
211
+ api_url( '/resources/json/delphix/' + name.to_s)
212
+ end
213
+ end
214
+
215
+ def self.validate(*args)
216
+ Base.validate(*args)
217
+ end
218
+
219
+ def self.optional(validation)
220
+ Validations::Optional.new(validation)
221
+ end
222
+
223
+ def self.many(validation)
224
+ Validations::Many.new(validation)
225
+ end
226
+
227
+ # @!visibility private
228
+ def self.to_s
229
+ "<#{self.class.name}:#{self.name}:#{object_id} @session=#{@session}>"
230
+ end
231
+ alias :inspect :to_s
232
+
233
+ # Hooks into the mixin process to add Croesus components to the given parent
234
+ # automatically.
235
+ #
236
+ # @param [Class] parent the object this is being mixed into
237
+ def self.included(parent)
238
+ parent.class_eval do
239
+ include InstanceMethods
240
+ extend ClassMethods
241
+ "<#{self.class.name}:#{self.name}:#{object_id} @session=#{@session}>"
242
+ end
243
+ end
244
+ private_class_method :included
245
+
246
+ # From where forth do ye descend!
247
+ def descendants
248
+ ObjectSpace.each_object(::Class).select {|klass| klass < self }
249
+ end
250
+ end