cfoundry 0.4.21 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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