jfoundry 0.1.0.pre
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.
- data/LICENSE +746 -0
- data/Rakefile +10 -0
- data/lib/cc_api_stub/applications.rb +53 -0
- data/lib/cc_api_stub/domains.rb +32 -0
- data/lib/cc_api_stub/frameworks.rb +22 -0
- data/lib/cc_api_stub/helper.rb +139 -0
- data/lib/cc_api_stub/login.rb +21 -0
- data/lib/cc_api_stub/organization_users.rb +21 -0
- data/lib/cc_api_stub/organizations.rb +70 -0
- data/lib/cc_api_stub/routes.rb +26 -0
- data/lib/cc_api_stub/runtimes.rb +22 -0
- data/lib/cc_api_stub/service_bindings.rb +22 -0
- data/lib/cc_api_stub/service_instances.rb +22 -0
- data/lib/cc_api_stub/services.rb +21 -0
- data/lib/cc_api_stub/spaces.rb +49 -0
- data/lib/cc_api_stub/users.rb +85 -0
- data/lib/cc_api_stub.rb +16 -0
- data/lib/jfoundry/auth_token.rb +63 -0
- data/lib/jfoundry/baseclient.rb +177 -0
- data/lib/jfoundry/chatty_hash.rb +46 -0
- data/lib/jfoundry/client.rb +39 -0
- data/lib/jfoundry/concerns/proxy_options.rb +17 -0
- data/lib/jfoundry/errors.rb +163 -0
- data/lib/jfoundry/rest_client.rb +331 -0
- data/lib/jfoundry/signature/version.rb +27 -0
- data/lib/jfoundry/signer.rb +13 -0
- data/lib/jfoundry/test_support.rb +3 -0
- data/lib/jfoundry/timer.rb +13 -0
- data/lib/jfoundry/trace_helpers.rb +64 -0
- data/lib/jfoundry/upload_helpers.rb +222 -0
- data/lib/jfoundry/v2/app.rb +357 -0
- data/lib/jfoundry/v2/app_event.rb +13 -0
- data/lib/jfoundry/v2/base.rb +92 -0
- data/lib/jfoundry/v2/client.rb +78 -0
- data/lib/jfoundry/v2/domain.rb +20 -0
- data/lib/jfoundry/v2/managed_service_instance.rb +13 -0
- data/lib/jfoundry/v2/model.rb +209 -0
- data/lib/jfoundry/v2/model_magic/attribute.rb +49 -0
- data/lib/jfoundry/v2/model_magic/client_extensions.rb +170 -0
- data/lib/jfoundry/v2/model_magic/has_summary.rb +49 -0
- data/lib/jfoundry/v2/model_magic/queryable_by.rb +39 -0
- data/lib/jfoundry/v2/model_magic/to_many.rb +138 -0
- data/lib/jfoundry/v2/model_magic/to_one.rb +81 -0
- data/lib/jfoundry/v2/model_magic.rb +93 -0
- data/lib/jfoundry/v2/organization.rb +22 -0
- data/lib/jfoundry/v2/quota_definition.rb +12 -0
- data/lib/jfoundry/v2/route.rb +25 -0
- data/lib/jfoundry/v2/service.rb +20 -0
- data/lib/jfoundry/v2/service_auth_token.rb +10 -0
- data/lib/jfoundry/v2/service_binding.rb +10 -0
- data/lib/jfoundry/v2/service_broker.rb +11 -0
- data/lib/jfoundry/v2/service_instance.rb +13 -0
- data/lib/jfoundry/v2/service_plan.rb +13 -0
- data/lib/jfoundry/v2/space.rb +18 -0
- data/lib/jfoundry/v2/stack.rb +10 -0
- data/lib/jfoundry/v2/user.rb +104 -0
- data/lib/jfoundry/v2/user_provided_service_instance.rb +7 -0
- data/lib/jfoundry/validator.rb +41 -0
- data/lib/jfoundry/version.rb +4 -0
- data/lib/jfoundry/zip.rb +56 -0
- data/lib/jfoundry.rb +5 -0
- data/lib/tasks/gem_release.rake +42 -0
- data/vendor/errors/README.md +3 -0
- data/vendor/errors/v1.yml +189 -0
- data/vendor/errors/v2.yml +470 -0
- metadata +269 -0
@@ -0,0 +1,63 @@
|
|
1
|
+
module JFoundry
|
2
|
+
class AuthToken
|
3
|
+
class << self
|
4
|
+
def from_uaa_token_info(token_info)
|
5
|
+
new(
|
6
|
+
token_info.auth_header,
|
7
|
+
token_info.info[:refresh_token]
|
8
|
+
)
|
9
|
+
end
|
10
|
+
|
11
|
+
def from_hash(hash)
|
12
|
+
new(
|
13
|
+
hash[:token],
|
14
|
+
hash[:refresh_token]
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(auth_header, refresh_token = nil)
|
20
|
+
@auth_header = auth_header
|
21
|
+
@refresh_token = refresh_token
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_accessor :auth_header
|
25
|
+
attr_reader :refresh_token
|
26
|
+
|
27
|
+
def to_hash
|
28
|
+
{
|
29
|
+
:token => auth_header,
|
30
|
+
:refresh_token => @refresh_token
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
JSON_HASH = /\{.*?\}/.freeze
|
35
|
+
|
36
|
+
# TODO: rename to #data
|
37
|
+
def token_data
|
38
|
+
return @token_data if @token_data
|
39
|
+
return {} unless @auth_header
|
40
|
+
|
41
|
+
json_hashes = Base64.decode64(@auth_header.split(" ", 2).last)
|
42
|
+
data_json = json_hashes.sub(JSON_HASH, "")[JSON_HASH]
|
43
|
+
return {} unless data_json
|
44
|
+
|
45
|
+
@token_data = MultiJson.load data_json, :symbolize_keys => true
|
46
|
+
rescue MultiJson::DecodeError
|
47
|
+
{}
|
48
|
+
end
|
49
|
+
|
50
|
+
def auth_header=(auth_header)
|
51
|
+
@token_data = nil
|
52
|
+
@auth_header = auth_header
|
53
|
+
end
|
54
|
+
|
55
|
+
def expiration
|
56
|
+
Time.at(token_data[:exp])
|
57
|
+
end
|
58
|
+
|
59
|
+
def expires_soon?
|
60
|
+
(expiration.to_i - Time.now.to_i) < 60
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require "jfoundry/trace_helpers"
|
2
|
+
require "net/https"
|
3
|
+
require "net/http/post/multipart"
|
4
|
+
require "multi_json"
|
5
|
+
require "fileutils"
|
6
|
+
require "forwardable"
|
7
|
+
|
8
|
+
module JFoundry
|
9
|
+
class BaseClient # :nodoc:
|
10
|
+
include JFoundry::ProxyOptions
|
11
|
+
|
12
|
+
extend Forwardable
|
13
|
+
|
14
|
+
attr_reader :rest_client
|
15
|
+
|
16
|
+
def_delegators :rest_client, :target, :target=, :access_key, :secret_key, :version, #:token,
|
17
|
+
:trace, :backtrace, :backtrace=, :log, :log=,
|
18
|
+
:http_proxy, :http_proxy=, :https_proxy, :https_proxy=
|
19
|
+
|
20
|
+
def initialize(target, access_key, secret_key, version)
|
21
|
+
@rest_client = JFoundry::RestClient.new(target, access_key, secret_key, version)
|
22
|
+
self.trace = false
|
23
|
+
self.backtrace = false
|
24
|
+
self.log = false
|
25
|
+
end
|
26
|
+
|
27
|
+
def trace=(trace)
|
28
|
+
@rest_client.trace = trace
|
29
|
+
#@uaa.trace = trace if @uaa
|
30
|
+
end
|
31
|
+
|
32
|
+
# Cloud metadata
|
33
|
+
def info
|
34
|
+
get("info", :accept => :json)
|
35
|
+
end
|
36
|
+
|
37
|
+
def get(*args)
|
38
|
+
request("GET", *args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete(*args)
|
42
|
+
request("DELETE", *args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def post(*args)
|
46
|
+
request("POST", *args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def put(*args)
|
50
|
+
request("PUT", *args)
|
51
|
+
end
|
52
|
+
|
53
|
+
def request(method, *args)
|
54
|
+
#puts "args: ", args
|
55
|
+
path, options = normalize_arguments(args)
|
56
|
+
#puts "path: ", path
|
57
|
+
#puts "options: ", options
|
58
|
+
request, response = request_raw(method, path, options)
|
59
|
+
handle_response(response, options, request)
|
60
|
+
end
|
61
|
+
|
62
|
+
def request_raw(method, path, options)
|
63
|
+
@rest_client.request(method, path, options)
|
64
|
+
end
|
65
|
+
|
66
|
+
def stream_url(url, &blk)
|
67
|
+
uri = URI.parse(url)
|
68
|
+
|
69
|
+
opts = {}
|
70
|
+
|
71
|
+
if uri.scheme == "https"
|
72
|
+
opts[:use_ssl] = true
|
73
|
+
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
|
74
|
+
end
|
75
|
+
|
76
|
+
Net::HTTP.start(uri.host, uri.port, *proxy_options_for(uri), opts) do |http|
|
77
|
+
http.read_timeout = 5
|
78
|
+
|
79
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
80
|
+
#req["Authorization"] = token.auth_header if token
|
81
|
+
|
82
|
+
http.request(req) do |response|
|
83
|
+
case response
|
84
|
+
when Net::HTTPOK
|
85
|
+
response.read_body(&blk)
|
86
|
+
when Net::HTTPNotFound
|
87
|
+
raise JFoundry::NotFound.new(response.body, 404)
|
88
|
+
when Net::HTTPForbidden
|
89
|
+
raise JFoundry::Denied.new(response.body, 403)
|
90
|
+
when Net::HTTPUnauthorized
|
91
|
+
raise JFoundry::Unauthorized.new(response.body, 401)
|
92
|
+
else
|
93
|
+
raise JFoundry::BadResponse.new(response.body, response.code)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def status_is_successful?(code)
|
102
|
+
(code >= 200) && (code < 400)
|
103
|
+
end
|
104
|
+
|
105
|
+
def handle_response(response, options, request)
|
106
|
+
if status_is_successful?(response[:status].to_i)
|
107
|
+
handle_successful_response(response, options)
|
108
|
+
else
|
109
|
+
handle_error_response(response, request)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def handle_successful_response(response, options)
|
114
|
+
if options[:return_response]
|
115
|
+
response
|
116
|
+
elsif options[:accept] == :json
|
117
|
+
parse_json(response[:body])
|
118
|
+
else
|
119
|
+
response[:body]
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def handle_error_response(response, request)
|
124
|
+
body_json = parse_json(response[:body])
|
125
|
+
body_code = body_json && body_json[:code]
|
126
|
+
code = body_code || response[:status].to_i
|
127
|
+
|
128
|
+
if body_code
|
129
|
+
error_class = JFoundry::APIError.error_classes[body_code] || JFoundry::APIError
|
130
|
+
raise error_class.new(body_json[:description], body_code, request, response)
|
131
|
+
end
|
132
|
+
|
133
|
+
case code
|
134
|
+
when 404
|
135
|
+
raise JFoundry::NotFound.new(nil, code, request, response)
|
136
|
+
when 403
|
137
|
+
raise JFoundry::Denied.new(nil, code, request, response)
|
138
|
+
when 401
|
139
|
+
raise JFoundry::Unauthorized.new(nil, code, request, response)
|
140
|
+
else
|
141
|
+
raise JFoundry::BadResponse.new(nil, code, request, response)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def normalize_arguments(args)
|
146
|
+
if args.last.is_a?(Hash)
|
147
|
+
options = args.pop
|
148
|
+
else
|
149
|
+
options = {}
|
150
|
+
end
|
151
|
+
|
152
|
+
[normalize_path(args), options]
|
153
|
+
end
|
154
|
+
|
155
|
+
URI_ENCODING_PATTERN = Regexp.new("[^#{URI::PATTERN::UNRESERVED}]")
|
156
|
+
|
157
|
+
def normalize_path(segments)
|
158
|
+
if segments.size == 1 && segments.first =~ /^\//
|
159
|
+
segments.first
|
160
|
+
else
|
161
|
+
segments.flatten.collect { |x|
|
162
|
+
URI.encode(x.to_s, URI_ENCODING_PATTERN)
|
163
|
+
}.join("/")
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def parse_json(x)
|
168
|
+
if x.empty?
|
169
|
+
raise MultiJson::DecodeError.new("Empty JSON string", [], "")
|
170
|
+
else
|
171
|
+
MultiJson.load(x, :symbolize_keys => true)
|
172
|
+
end
|
173
|
+
rescue MultiJson::DecodeError
|
174
|
+
nil
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module JFoundry
|
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
|
+
|
34
|
+
def to_hash
|
35
|
+
@hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
@hash.to_s
|
40
|
+
end
|
41
|
+
|
42
|
+
def inspect
|
43
|
+
@hash.inspect
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "jfoundry/concerns/proxy_options"
|
2
|
+
|
3
|
+
require "jfoundry/baseclient"
|
4
|
+
require "jfoundry/rest_client"
|
5
|
+
#require "jfoundry/auth_token"
|
6
|
+
|
7
|
+
require "jfoundry/v2/app"
|
8
|
+
require "jfoundry/v2/service"
|
9
|
+
require "jfoundry/v2/service_binding"
|
10
|
+
require "jfoundry/v2/managed_service_instance"
|
11
|
+
require "jfoundry/v2/user_provided_service_instance"
|
12
|
+
require "jfoundry/v2/service_plan"
|
13
|
+
#require "jfoundry/v2/service_auth_token"
|
14
|
+
require "jfoundry/v2/user"
|
15
|
+
require "jfoundry/v2/organization"
|
16
|
+
require "jfoundry/v2/space"
|
17
|
+
require "jfoundry/v2/domain"
|
18
|
+
require "jfoundry/v2/route"
|
19
|
+
require "jfoundry/v2/stack"
|
20
|
+
require "jfoundry/v2/quota_definition"
|
21
|
+
require "jfoundry/v2/app_event"
|
22
|
+
require "jfoundry/v2/service_broker"
|
23
|
+
|
24
|
+
require "jfoundry/v2/base"
|
25
|
+
require "jfoundry/v2/client"
|
26
|
+
#require "jfoundry/v2/fake_client"
|
27
|
+
|
28
|
+
module JFoundry
|
29
|
+
class Client < BaseClient
|
30
|
+
def self.new(*args)
|
31
|
+
warn "DEPRECATION WARNING: Please use JFoundry::Client.get instead of JFoundry::Client.new"
|
32
|
+
get(*args)
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.get(*args)
|
36
|
+
JFoundry::V2::Client.new(*args).tap { |client| client.info }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module JFoundry
|
2
|
+
module ProxyOptions
|
3
|
+
def proxy_options_for(uri)
|
4
|
+
ssl = uri.is_a?(URI::HTTPS)
|
5
|
+
proxy_to_use = (ssl ? https_proxy : http_proxy)
|
6
|
+
|
7
|
+
if proxy_to_use.blank?
|
8
|
+
[]
|
9
|
+
else
|
10
|
+
proxy_to_use = "proto://#{proxy_to_use}" unless proxy_to_use =~ /:\/\//
|
11
|
+
proxy_uri = URI.parse(proxy_to_use)
|
12
|
+
proxy_user, proxy_password = proxy_uri.userinfo.split(/:/) if proxy_uri.userinfo
|
13
|
+
[proxy_uri.host, proxy_uri.port, proxy_user, proxy_password]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require "net/https"
|
2
|
+
require "multi_json"
|
3
|
+
require "yaml"
|
4
|
+
|
5
|
+
module JFoundry
|
6
|
+
# Base class for JFoundry errors (not from the server).
|
7
|
+
class Error < RuntimeError
|
8
|
+
end
|
9
|
+
|
10
|
+
class Deprecated < Error
|
11
|
+
end
|
12
|
+
|
13
|
+
class Mismatch < Error
|
14
|
+
def initialize(expected, got)
|
15
|
+
@expected = expected
|
16
|
+
@got = got
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"Invalid value type; expected #{@expected.inspect}, got #{@got.inspect}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class InvalidTarget < Error
|
25
|
+
attr_reader :target
|
26
|
+
|
27
|
+
def initialize(target)
|
28
|
+
@target = target
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"Invalid target URI: #{@target}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class TargetRefused < Error
|
37
|
+
# Error message.
|
38
|
+
attr_reader :message
|
39
|
+
|
40
|
+
# Message varies as this represents various network errors.
|
41
|
+
def initialize(message)
|
42
|
+
@message = message
|
43
|
+
end
|
44
|
+
|
45
|
+
# Exception message.
|
46
|
+
def to_s
|
47
|
+
"target refused connection (#@message)"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Timeout < Timeout::Error
|
52
|
+
attr_reader :method, :uri, :parent
|
53
|
+
|
54
|
+
def initialize(method, uri, parent = nil)
|
55
|
+
@method = method
|
56
|
+
@uri = uri
|
57
|
+
@parent = parent
|
58
|
+
super(to_s)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_s
|
62
|
+
"#{method} #{uri} timed out"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Exception representing errors returned by the API.
|
67
|
+
class APIError < RuntimeError
|
68
|
+
include TraceHelpers
|
69
|
+
|
70
|
+
class << self
|
71
|
+
def error_classes
|
72
|
+
@error_classes ||= {}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
attr_reader :error_code, :description, :request, :response
|
77
|
+
|
78
|
+
# Create an APIError with a given request and response.
|
79
|
+
def initialize(description = nil, error_code = nil, request = nil, response = nil)
|
80
|
+
@response = response
|
81
|
+
@request = request
|
82
|
+
@error_code = error_code || (response ? response[:status] : nil)
|
83
|
+
@description = description || parse_description
|
84
|
+
end
|
85
|
+
|
86
|
+
# Exception message.
|
87
|
+
def to_s
|
88
|
+
"#{error_code}: #{description}"
|
89
|
+
end
|
90
|
+
|
91
|
+
def request_trace
|
92
|
+
super(request)
|
93
|
+
end
|
94
|
+
|
95
|
+
def response_trace
|
96
|
+
super(response)
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def parse_description
|
102
|
+
return unless response
|
103
|
+
|
104
|
+
parse_json(response[:body])[:description]
|
105
|
+
rescue MultiJson::DecodeError
|
106
|
+
response[:body]
|
107
|
+
end
|
108
|
+
|
109
|
+
def parse_json(x)
|
110
|
+
if x.empty?
|
111
|
+
raise MultiJson::DecodeError.new("Empty JSON string", [], "")
|
112
|
+
else
|
113
|
+
MultiJson.load(x, :symbolize_keys => true)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class NotFound < APIError
|
119
|
+
end
|
120
|
+
|
121
|
+
class Denied < APIError
|
122
|
+
end
|
123
|
+
|
124
|
+
class Unauthorized < APIError
|
125
|
+
end
|
126
|
+
|
127
|
+
class BadResponse < APIError
|
128
|
+
end
|
129
|
+
|
130
|
+
class UAAError < APIError
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.define_error(class_name, code)
|
134
|
+
base =
|
135
|
+
case class_name
|
136
|
+
when /NotFound$/
|
137
|
+
NotFound
|
138
|
+
else
|
139
|
+
APIError
|
140
|
+
end
|
141
|
+
|
142
|
+
klass =
|
143
|
+
if const_defined?(class_name)
|
144
|
+
const_get(class_name)
|
145
|
+
else
|
146
|
+
Class.new(base)
|
147
|
+
end
|
148
|
+
|
149
|
+
APIError.error_classes[code] = klass
|
150
|
+
|
151
|
+
unless const_defined?(class_name)
|
152
|
+
const_set(class_name, klass)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
VENDOR_DIR = File.expand_path("../../../vendor", __FILE__)
|
157
|
+
|
158
|
+
%w{errors/v1.yml errors/v2.yml}.each do |errors|
|
159
|
+
YAML.load_file("#{VENDOR_DIR}/#{errors}").each do |code, meta|
|
160
|
+
define_error(meta["name"], code)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|