cfoundry-IronFoundry 0.3.34

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require "rake"
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
4
+ require "cfoundry/version"
5
+
6
+ task :default => "spec"
7
+
8
+ desc "Run specs"
9
+ task "spec" => ["bundler:install", "test:spec"]
10
+
11
+ task :build do
12
+ sh "gem build cfoundry.gemspec"
13
+ end
14
+
15
+ task :install => :build do
16
+ sh "gem install --local cfoundry-#{CFoundry::VERSION}"
17
+ end
18
+
19
+ task :uninstall do
20
+ sh "gem uninstall cfoundry"
21
+ end
22
+
23
+ task :reinstall => [:uninstall, :install]
24
+
25
+ task :release => :build do
26
+ sh "gem push cfoundry-#{CFoundry::VERSION}.gem"
27
+ end
28
+
29
+ namespace "bundler" do
30
+ desc "Install gems"
31
+ task "install" do
32
+ sh("bundle install")
33
+ end
34
+ end
35
+
36
+ namespace "test" do
37
+ task "spec" do |t|
38
+ sh("cd spec && bundle exec rake spec")
39
+ end
40
+ end
data/lib/cfoundry.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "cfoundry/version"
2
+ require "cfoundry/client"
@@ -0,0 +1,235 @@
1
+ require "restclient"
2
+ require "multi_json"
3
+
4
+ module CFoundry
5
+ class BaseClient # :nodoc:
6
+ attr_accessor :trace, :no_backtrace
7
+
8
+ def initialize(target, token = nil)
9
+ @target = target
10
+ @token = token
11
+ @trace = false
12
+ @no_backtrace = false
13
+ end
14
+
15
+ def request_path(method, path, types = {}, options = {})
16
+ path = url(path) if path.is_a?(Array)
17
+
18
+ unless types.empty?
19
+ if params = types.delete(:params)
20
+ options[:params] = params
21
+ end
22
+
23
+ if types.size > 1
24
+ raise "request types must contain only one Content-Type => Accept"
25
+ end
26
+
27
+ options[:type] = types.keys.first
28
+ options[:accept] = types.values.first
29
+ end
30
+
31
+ request(method, path, options)
32
+ end
33
+
34
+ # grab the metadata from a token that looks like:
35
+ #
36
+ # bearer (base64 ...)
37
+ def token_data
38
+ tok = Base64.decode64(@token.sub(/^bearer\s+/, ""))
39
+ tok.sub!(/\{.+?\}/, "") # clear algo
40
+ MultiJson.load(tok[/\{.+?\}/], :symbolize_keys => true)
41
+
42
+ # normally i don't catch'em all, but can't expect all tokens to be the
43
+ # proper format, so just silently fail as this is not critical
44
+ rescue
45
+ {}
46
+ end
47
+
48
+ private
49
+
50
+ def parse_json(x)
51
+ MultiJson.load(x, :symbolize_keys => true)
52
+ end
53
+
54
+ def request(method, path, options = {})
55
+ accept = options.delete(:accept)
56
+ type = options.delete(:type)
57
+ payload = options.delete(:payload)
58
+ params = options.delete(:params)
59
+
60
+ headers = {}
61
+ headers["Authorization"] = @token if @token
62
+ headers["Proxy-User"] = @proxy if @proxy
63
+
64
+ if accept_type = mimetype(accept)
65
+ headers["Accept"] = accept_type
66
+ end
67
+
68
+ if content_type = mimetype(type)
69
+ headers["Content-Type"] = content_type
70
+ end
71
+
72
+ unless payload.is_a?(String)
73
+ case type
74
+ when :json
75
+ payload = MultiJson.dump(payload)
76
+ when :form
77
+ payload = encode_params(payload)
78
+ end
79
+ end
80
+
81
+ headers["Content-Length"] = payload ? payload.size : 0
82
+
83
+ headers.merge!(options[:headers]) if options[:headers]
84
+
85
+ if params
86
+ uri = URI.parse(path)
87
+ path += (uri.query ? "&" : "?") + encode_params(params)
88
+ end
89
+
90
+ req = options.dup
91
+ req[:method] = method
92
+ req[:url] = @target + path
93
+ req[:headers] = headers
94
+ req[:payload] = payload
95
+
96
+ json = accept == :json
97
+
98
+ RestClient::Request.execute(req) do |response, request|
99
+ print_trace(req, request, response, caller) if @trace
100
+ handle_response(response, accept)
101
+ end
102
+ rescue SocketError, Errno::ECONNREFUSED => e
103
+ raise TargetRefused, e.message
104
+ end
105
+
106
+ def mimetype(type)
107
+ case type
108
+ when String
109
+ type
110
+ when :json
111
+ "application/json"
112
+ when :form
113
+ "application/x-www-form-urlencoded"
114
+ when nil
115
+ nil
116
+ # return request headers (not really Accept)
117
+ when :headers
118
+ nil
119
+ else
120
+ raise "unknown mimetype #{type.inspect}"
121
+ end
122
+ end
123
+
124
+ def encode_params(hash, escape = true)
125
+ hash.keys.map do |k|
126
+ v = hash[k]
127
+ v = MultiJson.dump(v) if v.is_a?(Hash)
128
+ v = URI.escape(v.to_s, /[^#{URI::PATTERN::UNRESERVED}]/) if escape
129
+ "#{k}=#{v}"
130
+ end.join("&")
131
+ end
132
+
133
+ def request_with_types(method, path, options = {})
134
+ if path.last.is_a?(Hash)
135
+ types = path.pop
136
+ end
137
+
138
+ request_path(method, url(path), types || {}, options)
139
+ end
140
+
141
+ def get(*path)
142
+ request_with_types(:get, path)
143
+ end
144
+
145
+ def delete(*path)
146
+ request_with_types(:delete, path)
147
+ end
148
+
149
+ def post(payload, *path)
150
+ request_with_types(:post, path, :payload => payload)
151
+ end
152
+
153
+ def put(payload, *path)
154
+ request_with_types(:put, path, :payload => payload)
155
+ end
156
+
157
+ def url(segments)
158
+ "/#{safe_path(segments)}"
159
+ end
160
+
161
+ def safe_path(*segments)
162
+ segments.flatten.collect { |x|
163
+ URI.encode x.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
164
+ }.join("/")
165
+ end
166
+
167
+ def print_trace(req, request, response, locs)
168
+ $stderr.puts ">>>"
169
+ $stderr.puts "PROXY: #{RestClient.proxy}" if RestClient.proxy
170
+ $stderr.puts "REQUEST: #{req[:method]} #{req[:url]}"
171
+ $stderr.puts "RESPONSE_HEADERS:"
172
+ response.headers.each do |key, value|
173
+ $stderr.puts " #{key} : #{value}"
174
+ end
175
+ $stderr.puts "REQUEST_HEADERS:"
176
+ request.headers.each do |key, value|
177
+ $stderr.puts " #{key} : #{value}"
178
+ end
179
+ $stderr.puts "REQUEST_BODY: #{req[:payload]}" if req[:payload]
180
+ $stderr.puts "RESPONSE: [#{response.code}]"
181
+ begin
182
+ parsed_body = MultiJson.load(response.body)
183
+ $stderr.puts MultiJson.dump(parsed_body, :pretty => true)
184
+ rescue
185
+ $stderr.puts "#{response.body}"
186
+ end
187
+ $stderr.puts "<<<"
188
+
189
+ return if @no_backtrace
190
+
191
+ interesting_locs = locs.drop_while { |loc|
192
+ loc =~ /\/(cfoundry\/|restclient\/|net\/http)/
193
+ }
194
+
195
+ $stderr.puts "--- backtrace:"
196
+
197
+ $stderr.puts "... (boring)" unless locs == interesting_locs
198
+
199
+ trimmed_locs = interesting_locs[0..5]
200
+
201
+ trimmed_locs.each do |loc|
202
+ $stderr.puts "=== #{loc}"
203
+ end
204
+
205
+ $stderr.puts "... (trimmed)" unless trimmed_locs == interesting_locs
206
+ end
207
+
208
+ def handle_response(response, accept)
209
+ json = accept == :json
210
+
211
+ case response.code
212
+ when 200, 201, 204, 302
213
+ if accept == :headers
214
+ return response.headers
215
+ end
216
+
217
+ if json
218
+ if response.code == 204
219
+ raise "Expected JSON response, got 204 No Content"
220
+ end
221
+
222
+ parse_json(response)
223
+ else
224
+ response
225
+ end
226
+
227
+ when 404
228
+ raise CFoundry::NotFound
229
+
230
+ else
231
+ raise CFoundry::BadResponse.new(response.code, response)
232
+ end
233
+ end
234
+ end
235
+ end
@@ -0,0 +1,34 @@
1
+ module CFoundry
2
+ class ChattyHash
3
+ include Enumerable
4
+
5
+ def initialize(callback, hash = {})
6
+ @callback = callback
7
+ @hash = hash
8
+ end
9
+
10
+ def [](name)
11
+ @hash[name]
12
+ end
13
+
14
+ def []=(name, value)
15
+ @hash[name] = value
16
+ @callback.call(self)
17
+ value
18
+ end
19
+
20
+ def each(&blk)
21
+ @hash.each(&blk)
22
+ end
23
+
24
+ def delete(key)
25
+ value = @hash.delete(key)
26
+ @callback.call(self)
27
+ value
28
+ end
29
+
30
+ def to_json(*args)
31
+ @hash.to_json(*args)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ require "cfoundry/baseclient"
2
+
3
+ require "cfoundry/v1/client"
4
+ require "cfoundry/v2/client"
5
+
6
+ module CFoundry
7
+ class Client < BaseClient
8
+ def self.new(*args)
9
+ target, _ = args
10
+
11
+ base = super(target)
12
+
13
+ case base.info[:version]
14
+ when 2
15
+ CFoundry::V2::Client.new(*args)
16
+ else
17
+ CFoundry::V1::Client.new(*args)
18
+ end
19
+ end
20
+
21
+ def info
22
+ get("info", nil => :json)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,106 @@
1
+ module CFoundry
2
+ # Exception representing errors returned by the API.
3
+ class APIError < RuntimeError
4
+ class << self
5
+ # Generic error code for the exception.
6
+ attr_reader :error_code
7
+
8
+ # Generic description for the exception.
9
+ attr_reader :description
10
+
11
+ private
12
+
13
+ def setup(code, description = nil)
14
+ @error_code = code
15
+ @description = description
16
+ end
17
+ end
18
+
19
+ # Create an APIError with a given error code and description.
20
+ def initialize(error_code = nil, description = nil)
21
+ @error_code = error_code
22
+ @description = description
23
+ end
24
+
25
+ # A number representing the error.
26
+ def error_code
27
+ @error_code || self.class.error_code
28
+ end
29
+
30
+ # A description of the error.
31
+ def description
32
+ @description || self.class.description
33
+ end
34
+
35
+ # Exception message.
36
+ def to_s
37
+ if error_code
38
+ "#{error_code}: #{description}"
39
+ else
40
+ description
41
+ end
42
+ end
43
+ end
44
+
45
+ # Generic exception thrown when accessing something that doesn't exist (e.g.
46
+ # getting info of unknown application).
47
+ class NotFound < APIError
48
+ setup(404, "entity not found or inaccessible")
49
+ end
50
+
51
+ # Lower-level exception for when we cannot connect to the target.
52
+ class TargetRefused < APIError
53
+ @description = "target refused connection"
54
+
55
+ # Error message.
56
+ attr_reader :message
57
+
58
+ # Message varies as this represents various network errors.
59
+ def initialize(message)
60
+ @message = message
61
+ end
62
+
63
+ # Exception message.
64
+ def to_s
65
+ "#{description} (#{@message})"
66
+ end
67
+ end
68
+
69
+ # Exception raised when an application payload fails to upload.
70
+ class UploadFailed < APIError
71
+ setup(402)
72
+ end
73
+
74
+ # Exception raised when access is denied to something, either because the
75
+ # user is not logged in or is not an administrator.
76
+ class Denied < APIError
77
+ # Specific error code.
78
+ attr_reader :error_code
79
+
80
+ # Specific description.
81
+ attr_reader :description
82
+
83
+ # Initialize, with a default error code and message.
84
+ def initialize(
85
+ error_code = 200,
86
+ description = "Operation not permitted")
87
+ @error_code = error_code
88
+ @description = description
89
+ end
90
+ end
91
+
92
+ # Exception raised when the response is unexpected; usually from a server
93
+ # error.
94
+ class BadResponse < StandardError
95
+ # Initialize, with the HTTP response code and body.
96
+ def initialize(code, body = nil)
97
+ @code = code
98
+ @body = body
99
+ end
100
+
101
+ # Exception message.
102
+ def to_s
103
+ "target failed to handle our request due to an internal error (#{@code})"
104
+ end
105
+ end
106
+ end