croesus 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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