cfoundry 0.2.2 → 0.3.0

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,140 @@
1
+ require "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, :app_space, :service, :service_instance,
50
+ :user, :runtime, :framework].each do |obj|
51
+ plural = "#{obj}s"
52
+
53
+ define_method(obj) do |guid, *args|
54
+ depth, _ = args
55
+ depth ||= 1
56
+
57
+ params = { :"inline-relations-depth" => depth }
58
+
59
+ get("v2", plural, guid, nil => :json, :params => params)
60
+ end
61
+
62
+ define_method(:"create_#{obj}") do |payload|
63
+ post(payload, "v2", plural, :json => :json)
64
+ end
65
+
66
+ define_method(:"delete_#{obj}") do |guid|
67
+ delete("v2", plural, guid, nil => nil)
68
+ true
69
+ end
70
+
71
+ define_method(:"update_#{obj}") do |guid, payload|
72
+ put(payload, "v2", plural, guid, :json => :json)
73
+ end
74
+
75
+ define_method(plural) do |*args|
76
+ depth, query = args
77
+ depth ||= 1
78
+
79
+ params = { :"inline-relations-depth" => depth }
80
+
81
+ if query
82
+ params[:q] = "#{query.keys.first}:#{query.values.first}"
83
+ end
84
+
85
+ get("v2", plural, nil => :json, :params => params)
86
+ end
87
+ end
88
+
89
+ alias :spaces :app_spaces
90
+ alias :space :app_space
91
+ alias :update_space :update_app_space
92
+ alias :delete_space :delete_app_space
93
+ alias :create_space :create_app_space
94
+
95
+ private
96
+
97
+ def handle_response(response, accept)
98
+ json = accept == :json
99
+
100
+ case response.code
101
+ when 200, 201, 204, 302
102
+ if accept == :headers
103
+ return response.headers
104
+ end
105
+
106
+ if json
107
+ if response.code == 204
108
+ raise "Expected JSON response, got 204 No Content"
109
+ end
110
+
111
+ parse_json(response)
112
+ else
113
+ response
114
+ end
115
+
116
+ when 400
117
+ info = parse_json(response)
118
+ raise CFoundry::APIError.new(info[:code], info[:description])
119
+
120
+ when 401
121
+ info = parse_json(response)
122
+ raise CFoundry::Denied.new(info[:code], info[:description])
123
+
124
+ when 404
125
+ raise CFoundry::NotFound
126
+
127
+ when 411, 500, 504
128
+ begin
129
+ info = parse_json(response)
130
+ raise CFoundry::APIError.new(info[:code], info[:description])
131
+ rescue JSON::ParserError
132
+ raise CFoundry::BadResponse.new(response.code, response)
133
+ end
134
+
135
+ else
136
+ raise CFoundry::BadResponse.new(response.code, response)
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,184 @@
1
+ require "cfoundry/v2/base"
2
+
3
+ require "cfoundry/v2/app"
4
+ require "cfoundry/v2/framework"
5
+ require "cfoundry/v2/organization"
6
+ require "cfoundry/v2/runtime"
7
+ require "cfoundry/v2/service"
8
+ require "cfoundry/v2/service_instance"
9
+ require "cfoundry/v2/space"
10
+ require "cfoundry/v2/user"
11
+
12
+ module CFoundry::V2
13
+ # The primary API entrypoint. Wraps a BaseClient to provide nicer return
14
+ # values. Initialize with the target and, optionally, an auth token. These
15
+ # are the only two internal states.
16
+ class Client
17
+ # Internal BaseClient instance. Normally won't be touching this.
18
+ attr_reader :base
19
+
20
+ # [Organization] Currently targeted organization.
21
+ attr_accessor :current_organization
22
+
23
+ # [Space] Currently targeted space.
24
+ attr_accessor :current_space
25
+
26
+
27
+ # Create a new Client for interfacing with the given target.
28
+ #
29
+ # A token may also be provided to skip the login step.
30
+ def initialize(target = "http://api.cloudfoundry.com", token = nil)
31
+ @base = Base.new(target, token)
32
+ end
33
+
34
+ # The current target URL of the client.
35
+ def target
36
+ @base.target
37
+ end
38
+
39
+ # Current proxy user. Usually nil.
40
+ def proxy
41
+ @base.proxy
42
+ end
43
+
44
+ # Set the proxy user for the client. Must be authorized as an
45
+ # administrator for this to have any effect.
46
+ def proxy=(email)
47
+ @base.proxy = email
48
+ end
49
+
50
+ # Is the client tracing API requests?
51
+ def trace
52
+ @base.trace
53
+ end
54
+
55
+ # Set the tracing flag; if true, API requests and responses will be
56
+ # printed out.
57
+ def trace=(bool)
58
+ @base.trace = bool
59
+ end
60
+
61
+ # The currently authenticated user.
62
+ def current_user
63
+ if user = info[:user]
64
+ user(user)
65
+ end
66
+ end
67
+
68
+ # Cloud metadata
69
+ def info
70
+ @base.info
71
+ end
72
+
73
+ # Login prompts
74
+ def login_prompts
75
+ if @base.uaa
76
+ @base.uaa.prompts
77
+ else
78
+ { :username => ["text", "Email"],
79
+ :password => ["password", "Password"]
80
+ }
81
+ end
82
+ end
83
+
84
+ # Authenticate with the target. Sets the client token.
85
+ #
86
+ # Credentials is a hash, typically containing :username and :password
87
+ # keys.
88
+ #
89
+ # The values in the hash should mirror the prompts given by
90
+ # `login_prompts`.
91
+ def login(credentials)
92
+ @current_organization = nil
93
+ @current_space = nil
94
+
95
+ @base.token =
96
+ if @base.uaa
97
+ @base.uaa.authorize(credentials)
98
+ else
99
+ @base.create_token(
100
+ { :password => credentials[:password] },
101
+ credentials[:username])[:token]
102
+ end
103
+ end
104
+
105
+ # Clear client token. No requests are made for this.
106
+ def logout
107
+ @base.token = nil
108
+ end
109
+
110
+ # Is an authentication token set on the client?
111
+ def logged_in?
112
+ !!@base.token
113
+ end
114
+
115
+
116
+ [:app, :organization, :app_space, :service, :service_binding,
117
+ :service_instance, :user, :runtime, :framework].each do |singular|
118
+ klass = singular.to_s.capitalize.gsub(/(.)_(.)/) do
119
+ $1 + $2.upcase
120
+ end
121
+
122
+ plural = :"#{singular}s"
123
+
124
+ define_method(singular) do |*args|
125
+ id, _ = args
126
+ CFoundry::V2.const_get(klass).new(id, self)
127
+ end
128
+
129
+ define_method(plural) do |*args|
130
+ depth, query = args
131
+ depth ||= 1
132
+
133
+ @base.send(plural, depth, query)[:resources].collect do |json|
134
+ send(:"make_#{singular}", json)
135
+ end
136
+ end
137
+
138
+ define_method(:"#{singular}_from") do |path|
139
+ uri = URI.parse(path)
140
+
141
+ if uri.query
142
+ uri.query += "&inline-relations-depth=1"
143
+ else
144
+ uri.query = "inline-relations-depth=1"
145
+ end
146
+
147
+ send(
148
+ :"make_#{singular}",
149
+ @base.request_path(
150
+ :get,
151
+ uri.to_s,
152
+ nil => :json))
153
+ end
154
+
155
+ define_method(:"#{plural}_from") do |path|
156
+ uri = URI.parse(path)
157
+
158
+ if uri.query
159
+ uri.query += "&inline-relations-depth=1"
160
+ else
161
+ uri.query = "inline-relations-depth=1"
162
+ end
163
+
164
+ @base.request_path(
165
+ :get,
166
+ uri.to_s,
167
+ nil => :json)[:resources].collect do |json|
168
+ send(:"make_#{singular}", json)
169
+ end
170
+ end
171
+
172
+ define_method(:"make_#{singular}") do |json|
173
+ CFoundry::V2.const_get(klass).new(json[:metadata][:guid], self, json)
174
+ end
175
+ end
176
+
177
+ alias :spaces :app_spaces
178
+ alias :space :app_space
179
+
180
+ alias :system_frameworks :frameworks
181
+ alias :system_runtimes :runtimes
182
+ alias :system_services :services
183
+ end
184
+ end
@@ -0,0 +1,8 @@
1
+ require "cfoundry/v2/model"
2
+
3
+ module CFoundry::V2
4
+ class Domain < Model
5
+ attribute :name
6
+ to_one :organization
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ require "cfoundry/v2/model"
2
+
3
+ module CFoundry::V2
4
+ class Framework < Model
5
+ attribute :name
6
+ attribute :description
7
+ to_many :apps
8
+
9
+ def detection
10
+ nil # TODO for v2?
11
+ end
12
+
13
+ def runtimes
14
+ [] # TODO for v2?
15
+ end
16
+ end
17
+ end
18
+
@@ -0,0 +1,149 @@
1
+ module CFoundry::V2
2
+ class Model
3
+ class << self
4
+ def defaults
5
+ @defaults ||= {}
6
+ end
7
+
8
+ def attribute(name, opts = {})
9
+ default = opts[:default] || nil
10
+ defaults[name] = default if default
11
+
12
+ define_method(name) {
13
+ manifest[:entity][name] || default
14
+ }
15
+
16
+ define_method(:"#{name}=") { |val|
17
+ @manifest ||= {}
18
+ @manifest[:entity] ||= {}
19
+ @manifest[:entity][name] = val
20
+ }
21
+ end
22
+
23
+ def to_one(name, opts = {})
24
+ obj = opts[:as] || name
25
+
26
+ define_method(name) {
27
+ if manifest[:entity].key? name
28
+ @client.send(:"make_#{obj}", manifest[:entity][name])
29
+ else
30
+ @client.send(:"#{name}_from", send("#{obj}_url"))
31
+ end
32
+ }
33
+
34
+ define_method(:"#{name}_url") {
35
+ manifest[:entity][:"#{name}_url"]
36
+ }
37
+
38
+ define_method(:"#{name}=") { |x|
39
+ @manifest ||= {}
40
+ @manifest[:entity] ||= {}
41
+ @manifest[:entity][:"#{name}_guid"] = x.id
42
+ }
43
+ end
44
+
45
+ def to_many(plural, opts = {})
46
+ singular = plural.to_s.sub(/s$/, "").to_sym
47
+ object = opts[:as] || singular
48
+
49
+ define_method(plural) {
50
+ if manifest[:entity].key? plural
51
+ manifest[:entity][plural].collect do |json|
52
+ @client.send(:"make_#{object}", json)
53
+ end
54
+ else
55
+ @client.send(:"#{plural}_from", send("#{plural}_url"))
56
+ end
57
+ }
58
+
59
+ define_method(:"#{plural}_url") {
60
+ manifest[:entity][:"#{plural}_url"]
61
+ }
62
+
63
+ # TODO: these are hacky
64
+ define_method(:"add_#{singular}") { |x|
65
+ @client.base.request_path(
66
+ :put,
67
+ ["v2", "#{object_name}s", @id, plural, x.id],
68
+ nil => :json)
69
+ }
70
+
71
+ define_method(:"remove_#{singular}") {
72
+ @client.base.request_path(
73
+ :delete,
74
+ ["v2", "#{object_name}s", @id, plural, x.id],
75
+ nil => :json)
76
+ }
77
+
78
+ define_method(:"#{plural}=") { |xs|
79
+ @manifest ||= {}
80
+ @manifest[:entity] ||= {}
81
+ @manifest[:entity][:"#{singular}_guids"] = xs.collect(&:id)
82
+ }
83
+ end
84
+ end
85
+
86
+ attr_reader :id
87
+
88
+ def initialize(id, client, manifest = nil)
89
+ @id = id
90
+ @client = client
91
+ @manifest = manifest
92
+ end
93
+
94
+ def manifest
95
+ # inline depth of 2 for fewer requests
96
+ @manifest ||= @client.base.send(object_name, @id, 2)
97
+ end
98
+
99
+ def inspect
100
+ "\#<#{self.class.name} '#@id'>"
101
+ end
102
+
103
+ def object_name
104
+ @object_name ||=
105
+ self.class.name.split("::").last.gsub(
106
+ /([a-z])([A-Z])/,
107
+ '\1_\2').downcase
108
+ end
109
+
110
+ def create!
111
+ @manifest =
112
+ @client.base.send(
113
+ :"create_#{object_name}",
114
+ @manifest[:entity].merge(self.class.defaults))
115
+
116
+ @id = @manifest[:metadata][:guid]
117
+
118
+ true
119
+ end
120
+
121
+ def update!(diff = nil)
122
+ @client.base.send(
123
+ :"update_#{object_name}",
124
+ @id,
125
+ diff || manifest[:entity])
126
+
127
+ @manifest = nil
128
+ end
129
+
130
+ def delete!
131
+ @client.base.send(:"delete_#{object_name}", @id)
132
+
133
+ if @manifest
134
+ @manifest.delete :metadata
135
+ end
136
+ end
137
+
138
+ def exists?
139
+ @client.base.send(object_name, @id)
140
+ true
141
+ rescue CFoundry::APIError # TODO: NotFound would be better
142
+ false
143
+ end
144
+
145
+ def ==(other)
146
+ other.is_a?(self.class) && @id == other.id
147
+ end
148
+ end
149
+ end