cfoundry-IronFoundry 0.3.34

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.
@@ -0,0 +1,175 @@
1
+ require "multi_json"
2
+ require "base64"
3
+
4
+ require "cfoundry/baseclient"
5
+ require "cfoundry/uaaclient"
6
+
7
+ require "cfoundry/errors"
8
+
9
+ module CFoundry::V2
10
+ class Base < CFoundry::BaseClient
11
+ attr_accessor :target, :token, :proxy, :trace
12
+
13
+ def initialize(
14
+ target = "https://api.cloudfoundry.com",
15
+ token = nil)
16
+ @target = target
17
+ @token = token
18
+ end
19
+
20
+
21
+ # invalidate token data when changing token
22
+ def token=(t)
23
+ @token = t
24
+ end
25
+
26
+
27
+ # The UAA used for this client.
28
+ #
29
+ # `false` if no UAA (legacy)
30
+ def uaa
31
+ return @uaa unless @uaa.nil?
32
+
33
+ endpoint = info[:authorization_endpoint]
34
+ return @uaa = false unless endpoint
35
+
36
+ @uaa = CFoundry::UAAClient.new(endpoint)
37
+ @uaa.trace = @trace
38
+ @uaa.token = @token
39
+ @uaa
40
+ end
41
+
42
+
43
+ # Cloud metadata
44
+ def info
45
+ get("info", nil => :json)
46
+ end
47
+
48
+
49
+ [ :app, :organization, :space, :user, :runtime, :framework, :service,
50
+ :domain, :route, :service_plan, :service_binding, :service_instance,
51
+ :service_auth_token
52
+ ].each do |obj|
53
+ plural = "#{obj}s"
54
+
55
+ define_method(obj) do |guid, *args|
56
+ depth, _ = args
57
+ depth ||= 1
58
+
59
+ params = { :"inline-relations-depth" => depth }
60
+
61
+ get("v2", plural, guid, nil => :json, :params => params)
62
+ end
63
+
64
+ define_method(:"create_#{obj}") do |payload|
65
+ post(payload, "v2", plural, :json => :json)
66
+ end
67
+
68
+ define_method(:"delete_#{obj}") do |guid|
69
+ delete("v2", plural, guid, nil => nil)
70
+ true
71
+ end
72
+
73
+ define_method(:"update_#{obj}") do |guid, payload|
74
+ put(payload, "v2", plural, guid, :json => :json)
75
+ end
76
+
77
+ define_method(plural) do |*args|
78
+ get("v2", plural, nil => :json, :params => params_from(args))
79
+ end
80
+ end
81
+
82
+ def resource_match(fingerprints)
83
+ put(fingerprints, "v2", "resource_match", :json => :json)
84
+ end
85
+
86
+ def upload_app(guid, zipfile, resources = [])
87
+ payload = {
88
+ :resources => MultiJson.dump(resources),
89
+ :multipart => true,
90
+ :application =>
91
+ if zipfile.is_a? File
92
+ zipfile
93
+ elsif zipfile.is_a? String
94
+ File.new(zipfile, "rb")
95
+ end
96
+ }
97
+
98
+ put(payload, "v2", "apps", guid, "bits")
99
+ rescue RestClient::ServerBrokeConnection
100
+ retry
101
+ end
102
+
103
+ def files(guid, instance, *path)
104
+ get("v2", "apps", guid, "instances", instance, "files", *path)
105
+ end
106
+ alias :file :files
107
+
108
+ def instances(guid)
109
+ get("v2", "apps", guid, "instances", nil => :json)
110
+ end
111
+
112
+ def stats(guid)
113
+ get("v2", "apps", guid, "stats", nil => :json)
114
+ end
115
+
116
+
117
+ def params_from(args)
118
+ depth, query = args
119
+ depth ||= 1
120
+
121
+ params = { :"inline-relations-depth" => depth }
122
+
123
+ if query
124
+ params[:q] = "#{query.keys.first}:#{query.values.first}"
125
+ end
126
+
127
+ params
128
+ end
129
+
130
+ private
131
+
132
+ def handle_response(response, accept)
133
+ json = accept == :json
134
+
135
+ case response.code
136
+ when 200, 201, 204, 302
137
+ if accept == :headers
138
+ return response.headers
139
+ end
140
+
141
+ if json
142
+ if response.code == 204
143
+ raise "Expected JSON response, got 204 No Content"
144
+ end
145
+
146
+ parse_json(response)
147
+ else
148
+ response
149
+ end
150
+
151
+ when 400
152
+ info = parse_json(response)
153
+ raise CFoundry::APIError.new(info[:code], info[:description])
154
+
155
+ when 401, 403
156
+ info = parse_json(response)
157
+ raise CFoundry::Denied.new(info[:code], info[:description])
158
+
159
+ when 404
160
+ raise CFoundry::NotFound
161
+
162
+ when 411, 500, 504
163
+ begin
164
+ info = parse_json(response)
165
+ raise CFoundry::APIError.new(info[:code], info[:description])
166
+ rescue MultiJson::DecodeError
167
+ raise CFoundry::BadResponse.new(response.code, response)
168
+ end
169
+
170
+ else
171
+ raise CFoundry::BadResponse.new(response.code, response)
172
+ end
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,198 @@
1
+ require "multi_json"
2
+ require "base64"
3
+
4
+ require "cfoundry/v2/base"
5
+
6
+ require "cfoundry/v2/app"
7
+ require "cfoundry/v2/framework"
8
+ require "cfoundry/v2/organization"
9
+ require "cfoundry/v2/runtime"
10
+ require "cfoundry/v2/service"
11
+ require "cfoundry/v2/service_binding"
12
+ require "cfoundry/v2/service_instance"
13
+ require "cfoundry/v2/service_plan"
14
+ require "cfoundry/v2/service_auth_token"
15
+ require "cfoundry/v2/space"
16
+ require "cfoundry/v2/user"
17
+ require "cfoundry/v2/domain"
18
+ require "cfoundry/v2/route"
19
+
20
+ module CFoundry::V2
21
+ # The primary API entrypoint. Wraps a BaseClient to provide nicer return
22
+ # values. Initialize with the target and, optionally, an auth token. These
23
+ # are the only two internal states.
24
+ class Client
25
+ # Internal BaseClient instance. Normally won't be touching this.
26
+ attr_reader :base
27
+
28
+ # [Organization] Currently targeted organization.
29
+ attr_accessor :current_organization
30
+
31
+ # [Space] Currently targeted space.
32
+ attr_accessor :current_space
33
+
34
+
35
+ # Create a new Client for interfacing with the given target.
36
+ #
37
+ # A token may also be provided to skip the login step.
38
+ def initialize(target = "http://api.cloudfoundry.com", token = nil)
39
+ @base = Base.new(target, token)
40
+ end
41
+
42
+ # The current target URL of the client.
43
+ def target
44
+ @base.target
45
+ end
46
+
47
+ # Current proxy user. Usually nil.
48
+ def proxy
49
+ @base.proxy
50
+ end
51
+
52
+ # Set the proxy user for the client. Must be authorized as an
53
+ # administrator for this to have any effect.
54
+ def proxy=(email)
55
+ @base.proxy = email
56
+ end
57
+
58
+ # Is the client tracing API requests?
59
+ def trace
60
+ @base.trace
61
+ end
62
+
63
+ # Set the tracing flag; if true, API requests and responses will be
64
+ # printed out.
65
+ def trace=(bool)
66
+ @base.trace = bool
67
+ end
68
+
69
+ # The currently authenticated user.
70
+ def current_user
71
+ if guid = @base.token_data[:user_id]
72
+ user = user(guid)
73
+ user.emails = [{ :value => @base.token_data[:email] }]
74
+ user
75
+ end
76
+ end
77
+
78
+ # Cloud metadata
79
+ def info
80
+ @base.info
81
+ end
82
+
83
+ # Login prompts
84
+ def login_prompts
85
+ if @base.uaa
86
+ @base.uaa.prompts
87
+ else
88
+ { :username => ["text", "Email"],
89
+ :password => ["password", "Password"]
90
+ }
91
+ end
92
+ end
93
+
94
+ # Authenticate with the target. Sets the client token.
95
+ #
96
+ # Credentials is a hash, typically containing :username and :password
97
+ # keys.
98
+ #
99
+ # The values in the hash should mirror the prompts given by
100
+ # `login_prompts`.
101
+ def login(credentials)
102
+ @current_organization = nil
103
+ @current_space = nil
104
+
105
+ @base.token =
106
+ if @base.uaa
107
+ @base.uaa.authorize(credentials)
108
+ else
109
+ @base.create_token(
110
+ { :password => credentials[:password] },
111
+ credentials[:username])[:token]
112
+ end
113
+ end
114
+
115
+ # Clear client token. No requests are made for this.
116
+ def logout
117
+ @base.token = nil
118
+ end
119
+
120
+ # Is an authentication token set on the client?
121
+ def logged_in?
122
+ !!@base.token
123
+ end
124
+
125
+
126
+ [ :app, :organization, :space, :user, :runtime, :framework, :service,
127
+ :domain, :route, :service_plan, :service_binding, :service_instance,
128
+ :service_auth_token
129
+ ].each do |singular|
130
+ plural = :"#{singular}s"
131
+
132
+ classname = singular.to_s.capitalize.gsub(/(.)_(.)/) do
133
+ $1 + $2.upcase
134
+ end
135
+
136
+ klass = CFoundry::V2.const_get(classname)
137
+
138
+ has_space = klass.method_defined? :space
139
+ has_name = klass.method_defined? :name
140
+
141
+ define_method(singular) do |*args|
142
+ guid, _ = args
143
+ klass.new(guid, self)
144
+ end
145
+
146
+ define_method(plural) do |*args|
147
+ depth, query = args
148
+ depth ||= 1
149
+
150
+ if has_space && current_space
151
+ query ||= {}
152
+ query[:space_guid] ||= current_space.guid
153
+ end
154
+
155
+ @base.send(plural, depth, query)[:resources].collect do |json|
156
+ send(:"make_#{singular}", json)
157
+ end
158
+ end
159
+
160
+ if has_name
161
+ define_method(:"#{singular}_by_name") do |name|
162
+ if has_space && current_space
163
+ current_space.send(plural, 1, :name => name).first
164
+ else
165
+ send(plural, 1, :name => name).first
166
+ end
167
+ end
168
+ end
169
+
170
+ define_method(:"#{singular}_from") do |path, *args|
171
+ send(
172
+ :"make_#{singular}",
173
+ @base.request_path(
174
+ :get,
175
+ path,
176
+ nil => :json,
177
+ :params => @base.params_from(args)))
178
+ end
179
+
180
+ define_method(:"#{plural}_from") do |path, *args|
181
+ @base.request_path(
182
+ :get,
183
+ path,
184
+ nil => :json,
185
+ :params => @base.params_from(args))[:resources].collect do |json|
186
+ send(:"make_#{singular}", json)
187
+ end
188
+ end
189
+
190
+ define_method(:"make_#{singular}") do |json|
191
+ klass.new(
192
+ json[:metadata][:guid],
193
+ self,
194
+ json)
195
+ end
196
+ end
197
+ end
198
+ end
@@ -0,0 +1,8 @@
1
+ require "cfoundry/v2/model"
2
+
3
+ module CFoundry::V2
4
+ class Domain < Model
5
+ attribute :name, :string
6
+ to_one :owning_organization, :as => :organization
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ require "cfoundry/v2/model"
2
+
3
+ module CFoundry::V2
4
+ class Framework < Model
5
+ attribute :name, :string
6
+ attribute :description, :string
7
+ to_many :apps
8
+
9
+ attr_accessor :detection, :runtimes
10
+ end
11
+ end
12
+
@@ -0,0 +1,268 @@
1
+ require "multi_json"
2
+
3
+ module CFoundry::V2
4
+ class Model
5
+ class << self
6
+ def value_matches?(val, type)
7
+ case type
8
+ when Class
9
+ val.is_a?(type)
10
+ when Regexp
11
+ val.is_a?(String) && val =~ type
12
+ when :url
13
+ value_matches?(val, URI::regexp(%w(http https)))
14
+ when :https_url
15
+ value_matches?(val, URI::regexp("https"))
16
+ when :boolean
17
+ val.is_a?(TrueClass) || val.is_a?(FalseClass)
18
+ when Array
19
+ val.all? do |x|
20
+ value_matches?(x, type.first)
21
+ end
22
+ when Hash
23
+ val.is_a?(Hash) &&
24
+ type.all? { |name, subtype|
25
+ val.key?(name) && value_matches?(val[name], subtype)
26
+ }
27
+ else
28
+ val.is_a?(Object.const_get(type.to_s.capitalize))
29
+ end
30
+ end
31
+
32
+ def validate_type(val, type)
33
+ unless value_matches?(val, type)
34
+ raise "invalid attribute; expected #{type.inspect} but got #{val.inspect}"
35
+ end
36
+ end
37
+
38
+ def defaults
39
+ @defaults ||= {}
40
+ end
41
+
42
+ def attribute(name, type, opts = {})
43
+ default = opts[:default] || nil
44
+
45
+ has_default = opts.key?(:default)
46
+ defaults[name] = default if has_default
47
+
48
+ define_method(name) {
49
+ manifest[:entity][name] || default
50
+ }
51
+
52
+ define_method(:"#{name}=") { |val|
53
+ unless has_default && val == default
54
+ Model.validate_type(val, type)
55
+ end
56
+
57
+ @manifest ||= {}
58
+ @manifest[:entity] ||= {}
59
+ @manifest[:entity][name] = val
60
+ @diff[name] = val
61
+ }
62
+ end
63
+
64
+ def to_one(name, opts = {})
65
+ obj = opts[:as] || name
66
+ kls = obj.to_s.capitalize.gsub(/(.)_(.)/) do
67
+ $1 + $2.upcase
68
+ end
69
+
70
+ define_method(name) {
71
+ if @manifest && @manifest[:entity].key?(name)
72
+ @client.send(:"make_#{obj}", @manifest[:entity][name])
73
+ else
74
+ @client.send(
75
+ :"#{obj}_from",
76
+ send("#{name}_url"),
77
+ opts[:depth] || 1)
78
+ end
79
+ }
80
+
81
+ define_method(:"#{name}_url") {
82
+ manifest[:entity][:"#{name}_url"]
83
+ }
84
+
85
+ define_method(:"#{name}=") { |x|
86
+ Model.validate_type(x, CFoundry::V2.const_get(kls))
87
+
88
+ @manifest ||= {}
89
+ @manifest[:entity] ||= {}
90
+ @manifest[:entity][:"#{name}_guid"] =
91
+ @diff[:"#{name}_guid"] = x.guid
92
+ }
93
+ end
94
+
95
+ def to_many(plural, opts = {})
96
+ singular = plural.to_s.sub(/s$/, "").to_sym
97
+
98
+ object = opts[:as] || singular
99
+ plural_object = :"#{object}s"
100
+
101
+ kls = object.to_s.capitalize.gsub(/(.)_(.)/) do
102
+ $1 + $2.upcase
103
+ end
104
+
105
+ define_method(plural) { |*args|
106
+ depth, query = args
107
+
108
+ if @manifest && @manifest[:entity].key?(plural) && !depth
109
+ objs = @manifest[:entity][plural]
110
+
111
+ if query
112
+ find_by = query.keys.first
113
+ find_val = query.values.first
114
+ objs = objs.select { |o| o[:entity][find_by] == find_val }
115
+ end
116
+
117
+ objs.collect do |json|
118
+ @client.send(:"make_#{object}", json)
119
+ end
120
+ else
121
+ @client.send(
122
+ :"#{plural_object}_from",
123
+ "/v2/#{object_name}s/#@guid/#{plural}",
124
+ depth || opts[:depth],
125
+ query)
126
+ end
127
+ }
128
+
129
+ define_method(:"#{plural}_url") {
130
+ manifest[:entity][:"#{plural}_url"]
131
+ }
132
+
133
+ define_method(:"add_#{singular}") { |x|
134
+ # TODO: reflect this change in the app manifest?
135
+ Model.validate_type(x, CFoundry::V2.const_get(kls))
136
+
137
+ @client.base.request_path(
138
+ :put,
139
+ ["v2", "#{object_name}s", @guid, plural, x.guid],
140
+ nil => :json)
141
+ }
142
+
143
+ define_method(:"remove_#{singular}") { |x|
144
+ # TODO: reflect this change in the app manifest?
145
+ Model.validate_type(x, CFoundry::V2.const_get(kls))
146
+
147
+ @client.base.request_path(
148
+ :delete,
149
+ ["v2", "#{object_name}s", @guid, plural, x.guid],
150
+ nil => :json)
151
+ }
152
+
153
+ define_method(:"#{plural}=") { |xs|
154
+ Model.validate_type(x, [CFoundry::V2.const_get(kls)])
155
+
156
+ @manifest ||= {}
157
+ @manifest[:entity] ||= {}
158
+ @manifest[:entity][:"#{singular}_guids"] =
159
+ @diff[:"#{singular}_guids"] = xs.collect(&:guid)
160
+ }
161
+ end
162
+ end
163
+
164
+ attr_reader :guid
165
+
166
+ def initialize(guid, client, manifest = nil)
167
+ @guid = guid
168
+ @client = client
169
+ @manifest = manifest
170
+ @diff = {}
171
+ end
172
+
173
+ def manifest
174
+ @manifest ||= @client.base.send(object_name, @guid)
175
+ end
176
+
177
+ def inspect
178
+ "\#<#{self.class.name} '#@guid'>"
179
+ end
180
+
181
+ def object_name
182
+ @object_name ||=
183
+ self.class.name.split("::").last.gsub(
184
+ /([a-z])([A-Z])/,
185
+ '\1_\2').downcase
186
+ end
187
+
188
+ def invalidate!
189
+ @manifest = nil
190
+ @diff = {}
191
+ end
192
+
193
+ # this does a bit of extra processing to allow for
194
+ # `delete!' followed by `create!'
195
+ def create!
196
+ payload = {}
197
+
198
+ self.class.defaults.merge(@manifest[:entity]).each do |k, v|
199
+ if v.is_a?(Hash) && v.key?(:metadata)
200
+ # skip; there's a _guid attribute already
201
+ elsif v.is_a?(Array) && v.all? { |x|
202
+ x.is_a?(Hash) && x.key?(:metadata)
203
+ }
204
+ singular = k.to_s.sub(/s$/, "")
205
+
206
+ payload[:"#{singular}_guids"] = v.collect do |x|
207
+ if x.is_a?(Hash) && x.key?(:metadata)
208
+ x[:metadata][:guid]
209
+ else
210
+ x
211
+ end
212
+ end
213
+ elsif k.to_s.end_with?("_json") && v.is_a?(String)
214
+ payload[k] = MultiJson.load(v)
215
+ elsif k.to_s.end_with?("_url")
216
+ else
217
+ payload[k] = v
218
+ end
219
+ end
220
+
221
+ @manifest = @client.base.send(:"create_#{object_name}", payload)
222
+
223
+ @guid = @manifest[:metadata][:guid]
224
+
225
+ @diff.clear
226
+
227
+ true
228
+ end
229
+
230
+ def update!(diff = @diff)
231
+ @manifest = @client.base.send(:"update_#{object_name}", @guid, diff)
232
+
233
+ @diff.clear if diff == @diff
234
+
235
+ true
236
+ end
237
+
238
+ def delete!
239
+ @client.base.send(:"delete_#{object_name}", @guid)
240
+
241
+ @guid = nil
242
+
243
+ @diff.clear
244
+
245
+ if @manifest
246
+ @manifest.delete :metadata
247
+ end
248
+
249
+ true
250
+ end
251
+
252
+ def exists?
253
+ @client.base.send(object_name, @guid)
254
+ true
255
+ rescue CFoundry::APIError # TODO: NotFound would be better
256
+ false
257
+ end
258
+
259
+ def eql?(other)
260
+ other.is_a?(self.class) && @guid == other.guid
261
+ end
262
+ alias :== :eql?
263
+
264
+ def hash
265
+ @guid.hash
266
+ end
267
+ end
268
+ end