cfoundry 0.4.21 → 0.5.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.
Files changed (50) hide show
  1. data/Rakefile +47 -24
  2. data/lib/cfoundry/auth_token.rb +48 -0
  3. data/lib/cfoundry/baseclient.rb +96 -277
  4. data/lib/cfoundry/client.rb +2 -0
  5. data/lib/cfoundry/concerns/login_helpers.rb +13 -0
  6. data/lib/cfoundry/errors.rb +21 -13
  7. data/lib/cfoundry/rest_client.rb +290 -0
  8. data/lib/cfoundry/test_support.rb +3 -0
  9. data/lib/cfoundry/trace_helpers.rb +9 -9
  10. data/lib/cfoundry/uaaclient.rb +66 -74
  11. data/lib/cfoundry/upload_helpers.rb +2 -0
  12. data/lib/cfoundry/v1/app.rb +2 -2
  13. data/lib/cfoundry/v1/base.rb +4 -51
  14. data/lib/cfoundry/v1/client.rb +7 -30
  15. data/lib/cfoundry/v1/model.rb +22 -5
  16. data/lib/cfoundry/v1/model_magic.rb +30 -15
  17. data/lib/cfoundry/v2/app.rb +2 -5
  18. data/lib/cfoundry/v2/base.rb +10 -74
  19. data/lib/cfoundry/v2/client.rb +19 -30
  20. data/lib/cfoundry/v2/domain.rb +1 -4
  21. data/lib/cfoundry/v2/model.rb +1 -3
  22. data/lib/cfoundry/v2/model_magic.rb +13 -23
  23. data/lib/cfoundry/version.rb +1 -1
  24. data/lib/cfoundry/zip.rb +1 -1
  25. data/spec/cfoundry/auth_token_spec.rb +77 -0
  26. data/spec/cfoundry/baseclient_spec.rb +54 -30
  27. data/spec/cfoundry/errors_spec.rb +10 -13
  28. data/spec/cfoundry/rest_client_spec.rb +238 -0
  29. data/spec/cfoundry/trace_helpers_spec.rb +10 -5
  30. data/spec/cfoundry/uaaclient_spec.rb +141 -114
  31. data/spec/cfoundry/upload_helpers_spec.rb +129 -0
  32. data/spec/cfoundry/v1/base_spec.rb +2 -2
  33. data/spec/cfoundry/v1/client_spec.rb +17 -0
  34. data/spec/cfoundry/v1/model_magic_spec.rb +43 -0
  35. data/spec/cfoundry/v2/base_spec.rb +256 -33
  36. data/spec/cfoundry/v2/client_spec.rb +68 -0
  37. data/spec/cfoundry/v2/model_magic_spec.rb +49 -0
  38. data/spec/fixtures/apps/with_vmcignore/ignored_dir/file_in_ignored_dir.txt +1 -0
  39. data/spec/fixtures/apps/with_vmcignore/ignored_file.txt +1 -0
  40. data/spec/fixtures/apps/with_vmcignore/non_ignored_dir/file_in_non_ignored_dir.txt +1 -0
  41. data/spec/fixtures/apps/with_vmcignore/non_ignored_dir/ignored_file.txt +1 -0
  42. data/spec/fixtures/apps/with_vmcignore/non_ignored_file.txt +1 -0
  43. data/spec/fixtures/empty_file +0 -0
  44. data/spec/spec_helper.rb +4 -4
  45. data/spec/support/randoms.rb +3 -0
  46. data/spec/support/shared_examples/client_login_examples.rb +46 -0
  47. data/spec/support/{summaries.rb → shared_examples/model_summary_examples.rb} +0 -0
  48. data/spec/support/v1_fake_helper.rb +144 -0
  49. metadata +101 -37
  50. data/lib/cfoundry/spec_helper.rb +0 -1
@@ -51,6 +51,8 @@ module CFoundry
51
51
  FileUtils.rm_rf(tmpdir) if tmpdir
52
52
  end
53
53
 
54
+ private
55
+
54
56
  def prepare_package(path, to)
55
57
  if path =~ /\.(jar|war|zip)$/
56
58
  CFoundry::Zip.unpack(path, to)
@@ -10,8 +10,8 @@ module CFoundry::V1
10
10
  attribute :name, :string, :guid => true
11
11
  attribute :instances, :integer
12
12
  attribute :state, :string
13
- attribute :created, :integer, :at => [:meta, :created]
14
- attribute :version, :integer, :at => [:meta, :version]
13
+ attribute :created, :integer, :at => [:meta, :created], :read_only => true
14
+ attribute :version, :integer, :at => [:meta, :version], :read_only => true
15
15
  attribute :framework, :string, :at => [:staging, :model]
16
16
  attribute :runtime, :string, :at => [:staging, :stack]
17
17
  attribute :command, :string, :at => [:staging, :command]
@@ -9,36 +9,6 @@ module CFoundry::V1
9
9
  class Base < CFoundry::BaseClient
10
10
  include BaseClientMethods
11
11
 
12
- attr_accessor :target, :token, :proxy, :trace, :backtrace, :log
13
-
14
- def initialize(
15
- target = "https://api.cloudfoundry.com",
16
- token = nil)
17
- super
18
- end
19
-
20
-
21
- # The UAA used for this client.
22
- #
23
- # `false` if no UAA (legacy)
24
- def uaa
25
- return @uaa unless @uaa.nil?
26
-
27
- endpoint = info[:authorization_endpoint]
28
- return @uaa = false unless endpoint
29
-
30
- @uaa = CFoundry::UAAClient.new(endpoint)
31
- @uaa.trace = @trace
32
- @uaa.token = @token
33
- @uaa
34
- end
35
-
36
-
37
- # Cloud metadata
38
- def info
39
- get("info", :accept => :json)
40
- end
41
-
42
12
  def system_services
43
13
  get("services", "v1", "offerings", :content => :json, :accept => :json)
44
14
  end
@@ -50,12 +20,11 @@ module CFoundry::V1
50
20
  # Users
51
21
  def create_user(payload)
52
22
  # no JSON response
53
- post(payload, "users", :content => :json)
23
+ post("users", :content => :json, :payload => payload)
54
24
  end
55
25
 
56
26
  def create_token(payload, email)
57
- post(payload, "users", email, "tokens",
58
- :content => :json, :accept => :json)
27
+ post("users", email, "tokens", :content => :json, :accept => :json, :payload => payload)
59
28
  end
60
29
 
61
30
  # Applications
@@ -77,7 +46,7 @@ module CFoundry::V1
77
46
  end
78
47
 
79
48
  def resource_match(fingerprints)
80
- post(fingerprints, "resources", :content => :json, :accept => :json)
49
+ post("resources", :content => :json, :accept => :json, :payload => fingerprints)
81
50
  end
82
51
 
83
52
  def upload_app(name, zipfile, resources = [])
@@ -94,25 +63,9 @@ module CFoundry::V1
94
63
  "application/zip")
95
64
  }
96
65
 
97
- post(payload, "apps", name, "application")
66
+ post("apps", name, "application", :payload => payload)
98
67
  rescue EOFError
99
68
  retry
100
69
  end
101
-
102
- private
103
-
104
- def handle_response(response, accept, request)
105
- # this is a copy paste of v2
106
- return super if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
107
-
108
- info = parse_json(response.body)
109
- return super unless info[:code]
110
-
111
- cls = CFoundry::APIError.error_classes[info[:code]]
112
-
113
- raise (cls || CFoundry::APIError).new(request, response, info[:description], info[:code])
114
- rescue MultiJson::DecodeError
115
- super
116
- end
117
70
  end
118
71
  end
@@ -1,9 +1,11 @@
1
+ require File.expand_path("../../concerns/login_helpers", __FILE__)
2
+
1
3
  module CFoundry::V1
2
4
  # The primary API entrypoint. Wraps a BaseClient to provide nicer return
3
5
  # values. Initialize with the target and, optionally, an auth token. These
4
6
  # are the only two internal states.
5
7
  class Client
6
- include ClientMethods
8
+ include ClientMethods, CFoundry::LoginHelpers
7
9
 
8
10
  attr_reader :base
9
11
 
@@ -14,6 +16,10 @@ module CFoundry::V1
14
16
  @base = Base.new(target, token)
15
17
  end
16
18
 
19
+ def version
20
+ 1
21
+ end
22
+
17
23
  # The current target URL of the client.
18
24
  def target
19
25
  @base.target
@@ -162,35 +168,6 @@ module CFoundry::V1
162
168
  user(email)
163
169
  end
164
170
 
165
- # Login prompts
166
- def login_prompts
167
- if @base.uaa
168
- @base.uaa.prompts
169
- else
170
- { :username => ["text", "Email"],
171
- :password => ["password", "Password"]
172
- }
173
- end
174
- end
175
-
176
- # Authenticate with the target. Sets the client token.
177
- #
178
- # Credentials is a hash, typically containing :username and :password
179
- # keys.
180
- #
181
- # The values in the hash should mirror the prompts given by
182
- # `login_prompts`.
183
- def login(credentials)
184
- @base.token =
185
- if @base.uaa
186
- @base.uaa.authorize(credentials)
187
- else
188
- @base.create_token(
189
- { :password => credentials[:password] },
190
- credentials[:username])[:token]
191
- end
192
- end
193
-
194
171
  # Clear client token. No requests are made for this.
195
172
  def logout
196
173
  @base.token = nil
@@ -5,6 +5,8 @@ require "cfoundry/v1/model_magic"
5
5
 
6
6
  module CFoundry::V1
7
7
  class Model
8
+ @@objects = {}
9
+
8
10
  extend ModelMagic
9
11
 
10
12
  class << self
@@ -28,6 +30,15 @@ module CFoundry::V1
28
30
  def plural_base_object_name
29
31
  "#{base_object_name}s"
30
32
  end
33
+
34
+ def objects
35
+ @@objects
36
+ end
37
+
38
+ def inherited(klass)
39
+ @@objects[klass.object_name] = klass
40
+ super
41
+ end
31
42
  end
32
43
 
33
44
  attr_accessor :guid, :changes
@@ -79,8 +90,6 @@ module CFoundry::V1
79
90
  def update!
80
91
  @client.base.send(:"update_#{base_object_name}", @guid, write_manifest)
81
92
 
82
- invalidate!
83
-
84
93
  true
85
94
  end
86
95
 
@@ -108,6 +117,13 @@ module CFoundry::V1
108
117
  read[name] = val if found
109
118
  end
110
119
 
120
+ self.class.write_locations.each do |name, where|
121
+ next if self.class.read_only_attributes.include? name
122
+
123
+ found, val = find_path(manifest, where)
124
+ read[name] = val if found
125
+ end
126
+
111
127
  read[guid_name] = @guid
112
128
 
113
129
  read
@@ -129,10 +145,11 @@ module CFoundry::V1
129
145
  end
130
146
 
131
147
  def write_manifest(body = read_manifest, onto = {})
132
- onto[guid_name] = @guid
148
+ onto[guid_name] = @guid if guid_name
133
149
 
134
- self.class.write_locations.each do |what, where|
135
- put(body[what], onto, where) if body.key?(what)
150
+ self.class.write_locations.each do |name, where|
151
+ next if self.class.read_only_attributes.include? name
152
+ put(body[name], onto, where) if body.key?(name)
136
153
  end
137
154
 
138
155
  onto
@@ -18,6 +18,18 @@ module CFoundry::V1
18
18
  @write_locations ||= {}
19
19
  end
20
20
 
21
+ def read_only_attributes
22
+ @read_only_attributes ||= []
23
+ end
24
+
25
+ def on_client(&blk)
26
+ ClientMethods.module_eval(&blk)
27
+ end
28
+
29
+ def on_base_client(&blk)
30
+ BaseClientMethods.module_eval(&blk)
31
+ end
32
+
21
33
  def define_client_methods(klass = self)
22
34
  singular = klass.object_name
23
35
  plural = klass.plural_object_name
@@ -25,13 +37,13 @@ module CFoundry::V1
25
37
  base_singular = klass.base_object_name
26
38
  base_plural = klass.plural_base_object_name
27
39
 
28
- BaseClientMethods.module_eval do
40
+ on_base_client do
29
41
  define_method(base_singular) do |guid|
30
42
  get(base_plural, guid, :accept => :json)
31
43
  end
32
44
 
33
45
  define_method(:"create_#{base_singular}") do |payload|
34
- post(payload, base_plural, :content => :json, :accept => :json)
46
+ post(base_plural, :content => :json, :accept => :json, :payload => payload)
35
47
  end
36
48
 
37
49
  define_method(:"delete_#{base_singular}") do |guid|
@@ -40,7 +52,7 @@ module CFoundry::V1
40
52
  end
41
53
 
42
54
  define_method(:"update_#{base_singular}") do |guid, payload|
43
- put(payload, base_plural, guid, :content => :json)
55
+ put(base_plural, guid, :content => :json, :payload => payload)
44
56
  end
45
57
 
46
58
  define_method(base_plural) do |*args|
@@ -48,7 +60,7 @@ module CFoundry::V1
48
60
  end
49
61
  end
50
62
 
51
- ClientMethods.module_eval do
63
+ on_client do
52
64
  if klass.guid_name
53
65
  define_method(:"#{singular}_by_#{klass.guid_name}") do |guid|
54
66
  obj = send(singular, guid)
@@ -79,22 +91,20 @@ module CFoundry::V1
79
91
  write_only = opts[:write_only]
80
92
  has_default = opts.key?(:default)
81
93
 
82
- read_locations[name] = Array(opts[:read] || opts[:at] || name)
83
- write_locations[name] = Array(opts[:write] || opts[:at] || name)
94
+ read_locations[name] = Array(opts[:read] || opts[:at] || name) unless write_only
95
+ write_locations[name] = Array(opts[:write] || opts[:at] || name) unless read_only
96
+
97
+ read_only_attributes << name if read_only
84
98
 
85
99
  self.guid_name = name if is_guid
86
100
 
87
- unless write_only
88
- define_method(name) do
89
- return @guid if @guid && is_guid
101
+ define_method(name) do
102
+ return @guid if @guid && is_guid
90
103
 
91
- read = read_manifest
92
- read.key?(name) ? read[name] : default
93
- end
104
+ read = read_manifest
105
+ read.key?(name) ? read[name] : default
94
106
  end
95
107
 
96
- return if read_only
97
-
98
108
  define_method(:"#{name}=") do |val|
99
109
  unless has_default && val == default
100
110
  CFoundry::Validator.validate_type(val, type)
@@ -107,8 +117,13 @@ module CFoundry::V1
107
117
  old = read_manifest[name]
108
118
  @changes[name] = [old, val] if old != val
109
119
 
110
- put(val, @manifest, self.class.write_locations[name])
120
+ write_to = read_only ? self.class.read_locations : self.class.write_locations
121
+
122
+ put(val, @manifest, write_to[name])
111
123
  end
124
+
125
+ private name if write_only
126
+ private :"#{name}=" if read_only
112
127
  end
113
128
  end
114
129
  end
@@ -24,12 +24,12 @@ module CFoundry::V2
24
24
  to_one :framework
25
25
  attribute :environment_json, :hash, :default => {}
26
26
  attribute :memory, :integer, :default => 256
27
- attribute :instances, :integer, :default => 1
28
- attribute :file_descriptors, :integer, :default => 256
27
+ attribute :total_instances, :integer, :default => 1, :at => :instances
29
28
  attribute :disk_quota, :integer, :default => 256
30
29
  attribute :state, :string, :default => "STOPPED"
31
30
  attribute :command, :string, :default => nil
32
31
  attribute :console, :boolean, :default => false
32
+ attribute :buildpack, :string, :default => nil
33
33
  to_many :service_bindings
34
34
  to_many :routes
35
35
 
@@ -75,9 +75,6 @@ module CFoundry::V2
75
75
  end
76
76
  }
77
77
 
78
- alias :total_instances :instances
79
- alias :total_instances= :instances=
80
-
81
78
  private :environment_json
82
79
 
83
80
  def instances
@@ -9,39 +9,8 @@ module CFoundry::V2
9
9
  class Base < CFoundry::BaseClient
10
10
  include BaseClientMethods
11
11
 
12
- attr_accessor :target, :token, :proxy, :trace, :backtrace, :log
13
-
14
- def initialize(
15
- target = "https://api.cloudfoundry.com",
16
- token = nil)
17
- super
18
- end
19
-
20
-
21
- # The UAA used for this client.
22
- #
23
- # `false` if no UAA (legacy)
24
- def uaa
25
- return @uaa unless @uaa.nil?
26
-
27
- endpoint = info[:authorization_endpoint]
28
- return @uaa = false unless endpoint
29
-
30
- @uaa = CFoundry::UAAClient.new(endpoint)
31
- @uaa.trace = @trace
32
- @uaa.token = @token
33
- @uaa
34
- end
35
-
36
-
37
- # Cloud metadata
38
- def info
39
- get("info", :accept => :json)
40
- end
41
-
42
12
  def resource_match(fingerprints)
43
- put(fingerprints, "v2", "resource_match",
44
- :content => :json, :accept => :json)
13
+ put("v2", "resource_match", :content => :json, :accept => :json, :payload => fingerprints)
45
14
  end
46
15
 
47
16
  def upload_app(guid, zipfile, resources = [])
@@ -57,7 +26,7 @@ module CFoundry::V2
57
26
  "application/zip")
58
27
  }
59
28
 
60
- put(payload, "v2", "apps", guid, "bits")
29
+ put("v2", "apps", guid, "bits", :payload => payload)
61
30
  rescue EOFError
62
31
  retry
63
32
  end
@@ -68,27 +37,22 @@ module CFoundry::V2
68
37
  alias :file :files
69
38
 
70
39
  def stream_file(guid, instance, *path)
71
- redirect =
72
- request_with_options(
73
- Net::HTTP::Get,
74
- ["v2", "apps", guid, "instances", instance, "files", *path],
75
- :return_response => true)
40
+ path_and_options = path + [{:return_response => true, :follow_redirects => false}]
41
+ redirect = get("v2", "apps", guid, "instances", instance, "files", *path_and_options)
76
42
 
77
- if loc = redirect["location"]
78
- uri = URI.parse(loc + "&tail")
43
+ if location = redirect[:headers]["location"]
44
+ uri = URI.parse(location + "&tail")
79
45
 
80
46
  Net::HTTP.start(uri.host, uri.port) do |http|
81
47
  req = Net::HTTP::Get.new(uri.request_uri)
82
- req["Authorization"] = @token
48
+ req["Authorization"] = token.auth_header if token
83
49
 
84
50
  http.request(req) do |response|
85
- response.read_body do |chunk|
86
- yield chunk
87
- end
51
+ response.read_body { |chunk| yield chunk }
88
52
  end
89
53
  end
90
54
  else
91
- yield redirect.body
55
+ yield redirect[:body]
92
56
  end
93
57
  end
94
58
 
@@ -104,43 +68,15 @@ module CFoundry::V2
104
68
  get("v2", "apps", guid, "stats", :accept => :json)
105
69
  end
106
70
 
107
-
108
71
  def all_pages(paginated)
109
72
  payload = paginated[:resources]
110
73
 
111
74
  while next_page = paginated[:next_url]
112
- paginated = request_path(Net::HTTP::Get, next_page, :accept => :json)
75
+ paginated = get(next_page, :accept => :json)
113
76
  payload += paginated[:resources]
114
77
  end
115
78
 
116
79
  payload
117
80
  end
118
-
119
- private
120
-
121
- def handle_response(response, accept, request)
122
- # this is a copy paste of v1
123
- return super if response.is_a?(Net::HTTPSuccess) || response.is_a?(Net::HTTPRedirection)
124
-
125
- info = parse_json(response.body)
126
- return super unless info[:code]
127
-
128
- cls = CFoundry::APIError.error_classes[info[:code]]
129
-
130
- raise (cls || CFoundry::APIError).new(request, response, info[:description], info[:code])
131
- rescue MultiJson::DecodeError
132
- super
133
- end
134
-
135
- def log_line(io, data)
136
- io.printf(
137
- "[%s] %0.3fs %s %6s -> %d %s\n",
138
- Time.now.strftime("%F %T"),
139
- data[:time],
140
- data[:response][:headers]["x-vcap-request-id"],
141
- data[:request][:method].to_s.upcase,
142
- data[:response][:code],
143
- data[:request][:url])
144
- end
145
81
  end
146
82
  end