cf 0.1.5 → 0.6.0.rc1

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 (140) hide show
  1. data/LICENSE +1277 -30
  2. data/Rakefile +12 -1
  3. data/bin/cf +0 -3
  4. data/lib/cf.rb +6 -0
  5. data/lib/cf/cli.rb +389 -190
  6. data/lib/cf/cli/app/app.rb +45 -0
  7. data/lib/cf/cli/app/apps.rb +99 -0
  8. data/lib/cf/cli/app/base.rb +90 -0
  9. data/lib/cf/cli/app/crashes.rb +42 -0
  10. data/lib/cf/cli/app/delete.rb +95 -0
  11. data/lib/cf/cli/app/deprecated.rb +11 -0
  12. data/lib/cf/cli/app/env.rb +78 -0
  13. data/lib/cf/cli/app/files.rb +137 -0
  14. data/lib/cf/cli/app/health.rb +26 -0
  15. data/lib/cf/cli/app/instances.rb +53 -0
  16. data/lib/cf/cli/app/logs.rb +76 -0
  17. data/lib/cf/cli/app/push.rb +105 -0
  18. data/lib/cf/cli/app/push/create.rb +149 -0
  19. data/lib/cf/cli/app/push/interactions.rb +94 -0
  20. data/lib/cf/cli/app/push/sync.rb +64 -0
  21. data/lib/cf/cli/app/rename.rb +35 -0
  22. data/lib/cf/cli/app/restart.rb +20 -0
  23. data/lib/cf/cli/app/scale.rb +69 -0
  24. data/lib/cf/cli/app/start.rb +143 -0
  25. data/lib/cf/cli/app/stats.rb +67 -0
  26. data/lib/cf/cli/app/stop.rb +27 -0
  27. data/lib/cf/cli/domain/base.rb +8 -0
  28. data/lib/cf/cli/domain/domains.rb +40 -0
  29. data/lib/cf/cli/domain/map.rb +55 -0
  30. data/lib/cf/cli/domain/unmap.rb +56 -0
  31. data/lib/cf/cli/help.rb +15 -0
  32. data/lib/cf/cli/interactive.rb +105 -0
  33. data/lib/cf/cli/organization/base.rb +12 -0
  34. data/lib/cf/cli/organization/create.rb +32 -0
  35. data/lib/cf/cli/organization/delete.rb +73 -0
  36. data/lib/cf/cli/organization/org.rb +45 -0
  37. data/lib/cf/cli/organization/orgs.rb +35 -0
  38. data/lib/cf/cli/organization/rename.rb +36 -0
  39. data/lib/cf/cli/route/base.rb +8 -0
  40. data/lib/cf/cli/route/map.rb +70 -0
  41. data/lib/cf/cli/route/routes.rb +26 -0
  42. data/lib/cf/cli/route/unmap.rb +62 -0
  43. data/lib/cf/cli/service/base.rb +8 -0
  44. data/lib/cf/cli/service/bind.rb +44 -0
  45. data/lib/cf/cli/service/create.rb +107 -0
  46. data/lib/cf/cli/service/delete.rb +82 -0
  47. data/lib/cf/cli/service/rename.rb +35 -0
  48. data/lib/cf/cli/service/service.rb +40 -0
  49. data/lib/cf/cli/service/services.rb +99 -0
  50. data/lib/cf/cli/service/unbind.rb +38 -0
  51. data/lib/cf/cli/space/base.rb +19 -0
  52. data/lib/cf/cli/space/create.rb +63 -0
  53. data/lib/cf/cli/space/delete.rb +95 -0
  54. data/lib/cf/cli/space/rename.rb +39 -0
  55. data/lib/cf/cli/space/space.rb +64 -0
  56. data/lib/cf/cli/space/spaces.rb +55 -0
  57. data/lib/cf/cli/space/switch.rb +16 -0
  58. data/lib/cf/cli/start/base.rb +93 -0
  59. data/lib/cf/cli/start/colors.rb +13 -0
  60. data/lib/cf/cli/start/info.rb +124 -0
  61. data/lib/cf/cli/start/login.rb +94 -0
  62. data/lib/cf/cli/start/logout.rb +17 -0
  63. data/lib/cf/cli/start/target.rb +69 -0
  64. data/lib/cf/cli/start/target_interactions.rb +37 -0
  65. data/lib/cf/cli/start/targets.rb +16 -0
  66. data/lib/cf/cli/user/base.rb +29 -0
  67. data/lib/cf/cli/user/create.rb +39 -0
  68. data/lib/cf/cli/user/passwd.rb +43 -0
  69. data/lib/cf/cli/user/register.rb +42 -0
  70. data/lib/cf/cli/user/users.rb +32 -0
  71. data/lib/cf/constants.rb +10 -7
  72. data/lib/cf/detect.rb +113 -48
  73. data/lib/cf/errors.rb +17 -0
  74. data/lib/cf/plugin.rb +28 -12
  75. data/lib/cf/spacing.rb +89 -0
  76. data/lib/cf/spec_helper.rb +1 -0
  77. data/lib/cf/test_support.rb +6 -0
  78. data/lib/cf/version.rb +1 -1
  79. data/spec/assets/hello-sinatra/Gemfile +3 -0
  80. data/spec/assets/hello-sinatra/Gemfile.lock +17 -0
  81. data/spec/assets/hello-sinatra/config.ru +3 -0
  82. data/spec/assets/hello-sinatra/fat-cat-makes-app-larger.png +0 -0
  83. data/spec/assets/hello-sinatra/main.rb +6 -0
  84. data/spec/assets/specker_runner/specker_runner_input.rb +6 -0
  85. data/spec/assets/specker_runner/specker_runner_pause.rb +5 -0
  86. data/spec/cf/cli/app/base_spec.rb +17 -0
  87. data/spec/cf/cli/app/delete_spec.rb +188 -0
  88. data/spec/cf/cli/app/instances_spec.rb +65 -0
  89. data/spec/cf/cli/app/push/create_spec.rb +661 -0
  90. data/spec/cf/cli/app/push_spec.rb +369 -0
  91. data/spec/cf/cli/app/rename_spec.rb +104 -0
  92. data/spec/cf/cli/app/scale_spec.rb +75 -0
  93. data/spec/cf/cli/app/start_spec.rb +208 -0
  94. data/spec/cf/cli/app/stats_spec.rb +68 -0
  95. data/spec/cf/cli/domain/map_spec.rb +130 -0
  96. data/spec/cf/cli/domain/unmap_spec.rb +69 -0
  97. data/spec/cf/cli/organization/orgs_spec.rb +108 -0
  98. data/spec/cf/cli/organization/rename_spec.rb +113 -0
  99. data/spec/cf/cli/route/map_spec.rb +121 -0
  100. data/spec/cf/cli/route/unmap_spec.rb +155 -0
  101. data/spec/cf/cli/service/bind_spec.rb +25 -0
  102. data/spec/cf/cli/service/delete_spec.rb +22 -0
  103. data/spec/cf/cli/service/rename_spec.rb +105 -0
  104. data/spec/cf/cli/service/service_spec.rb +23 -0
  105. data/spec/cf/cli/service/unbind_spec.rb +25 -0
  106. data/spec/cf/cli/space/create_spec.rb +93 -0
  107. data/spec/cf/cli/space/rename_spec.rb +102 -0
  108. data/spec/cf/cli/space/spaces_spec.rb +104 -0
  109. data/spec/cf/cli/space/switch_space_spec.rb +55 -0
  110. data/spec/cf/cli/start/info_spec.rb +160 -0
  111. data/spec/cf/cli/start/login_spec.rb +142 -0
  112. data/spec/cf/cli/start/logout_spec.rb +50 -0
  113. data/spec/cf/cli/start/target_spec.rb +123 -0
  114. data/spec/cf/cli/user/create_spec.rb +54 -0
  115. data/spec/cf/cli/user/passwd_spec.rb +102 -0
  116. data/spec/cf/cli/user/register_spec.rb +140 -0
  117. data/spec/cf/cli_spec.rb +442 -0
  118. data/spec/cf/detect_spec.rb +54 -0
  119. data/spec/console_app_specker/console_app_specker_matchers_spec.rb +173 -0
  120. data/spec/console_app_specker/specker_runner_spec.rb +167 -0
  121. data/spec/features/account_lifecycle_spec.rb +85 -0
  122. data/spec/features/login_spec.rb +66 -0
  123. data/spec/features/push_flow_spec.rb +125 -0
  124. data/spec/features/switching_targets_spec.rb +32 -0
  125. data/spec/spec_helper.rb +72 -0
  126. data/spec/support/command_helper.rb +81 -0
  127. data/spec/support/config_helper.rb +15 -0
  128. data/spec/support/console_app_specker_matchers.rb +86 -0
  129. data/spec/support/fake_home_dir.rb +55 -0
  130. data/spec/support/interact_helper.rb +29 -0
  131. data/spec/support/shared_examples/errors.rb +40 -0
  132. data/spec/support/shared_examples/input.rb +14 -0
  133. data/spec/support/specker_runner.rb +80 -0
  134. data/spec/support/tracking_expector.rb +71 -0
  135. metadata +427 -66
  136. data/lib/cf/cli/app.rb +0 -595
  137. data/lib/cf/cli/command.rb +0 -444
  138. data/lib/cf/cli/dots.rb +0 -133
  139. data/lib/cf/cli/service.rb +0 -112
  140. data/lib/cf/cli/user.rb +0 -71
data/Rakefile CHANGED
@@ -1 +1,12 @@
1
- require "bundler/gem_tasks"
1
+ require "rake"
2
+ require "rspec/core/rake_task"
3
+
4
+ specfile, _ = Dir["*.gemspec"]
5
+ SPEC = Gem::Specification.load(specfile)
6
+ CURRENT_VERSION = SPEC.version.to_s.freeze
7
+
8
+ RSpec::Core::RakeTask.new(:spec)
9
+ task :default => :spec
10
+
11
+ # looking for a way to push gems? check out the new frontend-release git repo!
12
+ # git@github.com:pivotal-vmware/frontend-release.git
data/bin/cf CHANGED
@@ -2,13 +2,10 @@
2
2
  # vim: ft=ruby
3
3
 
4
4
  require "rubygems"
5
- require "thor"
6
5
 
7
6
  require "cf"
8
7
  require "cf/plugin"
9
8
 
10
9
  CF::Plugin.load_all
11
10
 
12
- $exit_status = 0
13
11
  CF::CLI.start(ARGV)
14
- exit($exit_status)
data/lib/cf.rb CHANGED
@@ -1,2 +1,8 @@
1
1
  require "cf/version"
2
+
2
3
  require "cf/cli"
4
+
5
+ command_files = "../cf/cli/{app,route,domain,organization,space,service,start,user}/*.rb"
6
+ Dir[File.expand_path(command_files, __FILE__)].each do |file|
7
+ require file unless File.basename(file) == 'base.rb'
8
+ end
@@ -1,270 +1,469 @@
1
- require "cf/cli/command"
2
- require "cf/cli/app"
3
- require "cf/cli/service"
4
- require "cf/cli/user"
1
+ require "yaml"
2
+ require "socket"
3
+ require "net/http"
4
+ require "multi_json"
5
+ require "fileutils"
6
+
7
+ require "mothership"
8
+
9
+ require "cfoundry"
10
+
11
+ require "cf/constants"
12
+ require "cf/errors"
13
+ require "cf/spacing"
14
+
15
+ require "cf/cli/help"
16
+ require "cf/cli/interactive"
17
+
18
+
19
+ $cf_asked_auth = false
5
20
 
6
21
  module CF
7
- class CLI < App # subclass App since we operate on Apps by default
8
- class_option :proxy, :aliases => "-u", :desc => "Proxy user"
22
+ class CLI < Mothership
23
+ include CF::Interactive
24
+ include CF::Spacing
9
25
 
10
- class_option :verbose,
11
- :type => :boolean, :aliases => "-v", :desc => "Verbose"
26
+ option :help, :desc => "Show command usage", :alias => "-h",
27
+ :default => false
12
28
 
13
- class_option :force,
14
- :type => :boolean, :aliases => "-f", :desc => "Force (no interaction)"
29
+ option :proxy, :desc => "Run this command as another user (admin)", :alias => "-u",
30
+ :value => :email
15
31
 
16
- class_option :simple_output,
17
- :type => :boolean, :desc => "Simplified output format."
32
+ option :version, :desc => "Print version number", :alias => "-v",
33
+ :default => false
18
34
 
19
- class_option :script, :type => :boolean, :aliases => "-s",
20
- :desc => "--simple-output and --force"
35
+ option :verbose, :desc => "Print extra information", :alias => "-V",
36
+ :default => false
21
37
 
22
- class_option :color, :type => :boolean, :desc => "Colored output"
38
+ option :force, :desc => "Skip interaction when possible", :alias => "-f",
39
+ :type => :boolean, :default => proc { input[:script] }
23
40
 
24
- desc "service SUBCOMMAND ...ARGS", "Manage your services"
25
- subcommand "service", Service
41
+ option :debug, :desc => "Print full stack trace (instead of crash log)",
42
+ :type => :boolean, :default => false
26
43
 
27
- desc "user SUBCOMMAND ...ARGS", "User management"
28
- subcommand "user", User
44
+ option :quiet, :desc => "Simplify output format", :alias => "-q",
45
+ :type => :boolean, :default => proc { input[:script] }
29
46
 
30
- desc "info", "Display information on the current target, user, et."
31
- flag(:runtimes)
32
- flag(:services)
33
- def info
34
- info =
35
- with_progress("Getting target information") do
36
- client.info
37
- end
47
+ option :script, :desc => "Shortcut for --quiet and --force",
48
+ :type => :boolean, :default => proc { !$stdout.tty? }
38
49
 
39
- if input(:runtimes)
40
- runtimes = {}
41
- info["frameworks"].each do |_, f|
42
- f["runtimes"].each do |r|
43
- runtimes[r["name"]] = r
44
- end
45
- end
50
+ option :color, :desc => "Use colorful output",
51
+ :type => :boolean, :default => proc { !input[:quiet] }
46
52
 
47
- runtimes = runtimes.values.sort_by { |x| x["name"] }
53
+ option :trace, :desc => "Show API traffic", :alias => "-t",
54
+ :default => false
48
55
 
49
- if simple_output?
50
- runtimes.each do |r|
51
- puts r["name"]
52
- end
53
- return
54
- end
55
56
 
56
- runtimes.each do |r|
57
- puts ""
58
- puts "#{c(r["name"], :blue)}:"
59
- puts " version: #{b(r["version"])}"
60
- puts " description: #{b(r["description"])}"
61
- end
57
+ def default_action
58
+ if input[:version]
59
+ line "cf #{VERSION}"
60
+ else
61
+ super
62
+ end
63
+ end
62
64
 
63
- return
65
+ def check_target
66
+ unless client && client.target
67
+ fail "Please select a target with 'cf target'."
64
68
  end
69
+ end
65
70
 
66
- if input(:services)
67
- services = {}
68
- client.system_services.each do |_, svcs|
69
- svcs.each do |name, versions|
70
- services[name] = versions.values
71
- end
71
+ def check_logged_in
72
+ unless client.logged_in?
73
+ if force?
74
+ fail "Please log in with 'cf login'."
75
+ else
76
+ line c("Please log in first to proceed.", :warning)
77
+ line
78
+ invoke :login
79
+ invalidate_client
72
80
  end
81
+ end
82
+ end
73
83
 
74
- if simple_output?
75
- services.each do |name, _|
76
- puts name
77
- end
84
+ def precondition
85
+ check_target
86
+ check_logged_in
78
87
 
79
- return
80
- end
88
+ unless client.current_organization
89
+ fail "Please select an organization with 'cf target --ask-org'."
90
+ end
81
91
 
82
- services.each do |name, versions|
83
- puts ""
84
- puts "#{c(name, :blue)}:"
85
- puts " versions: #{versions.collect { |v| v["version"] }.join ", "}"
86
- puts " description: #{versions[0]["description"]}"
87
- puts " type: #{versions[0]["type"]}"
88
- end
92
+ unless client.current_space
93
+ fail "Please select a space with 'cf target --ask-space'."
94
+ end
95
+ end
89
96
 
90
- return
97
+ def wrap_errors
98
+ yield
99
+ rescue CFoundry::Timeout => e
100
+ err(e.message)
101
+ rescue Interrupt
102
+ exit_status 130
103
+ rescue Mothership::Error
104
+ raise
105
+ rescue UserError => e
106
+ log_error(e)
107
+ err e.message
108
+ rescue SystemExit
109
+ raise
110
+ rescue CFoundry::Forbidden, CFoundry::InvalidAuthToken => e
111
+ if !$cf_asked_auth
112
+ $cf_asked_auth = true
113
+
114
+ line
115
+ line c("Not authenticated! Try logging in:", :warning)
116
+
117
+ # TODO: there's no color here; global flags not being passed
118
+ # through (mothership bug?)
119
+ invoke :login
120
+
121
+ retry
91
122
  end
92
123
 
93
- puts ""
124
+ log_error(e)
94
125
 
95
- puts info["description"]
96
- puts ""
97
- puts "target: #{b(client.target)}"
98
- puts " version: #{info["version"]}"
99
- puts " support: #{info["support"]}"
126
+ err "Denied: #{e.description}"
100
127
 
101
- if info["user"]
102
- puts ""
103
- puts "user: #{b(info["user"])}"
104
- puts " usage:"
128
+ rescue Exception => e
129
+ log_error(e)
105
130
 
106
- limits = info["limits"]
107
- info["usage"].each do |k, v|
108
- m = limits[k]
109
- if k == "memory"
110
- puts " #{k}: #{usage(v * 1024 * 1024, m * 1024 * 1024)}"
111
- else
112
- puts " #{k}: #{b(v)} of #{b(m)} limit"
131
+ msg = e.class.name
132
+ msg << ": #{e}" unless e.to_s.empty?
133
+ msg << "\nFor more information, see #{CF::CRASH_FILE}"
134
+ err msg
135
+
136
+ raise if debug?
137
+ end
138
+
139
+ def execute(cmd, argv, global = {})
140
+ if input[:help]
141
+ invoke :help, :command => cmd.name.to_s
142
+ else
143
+ wrap_errors do
144
+ @command = cmd
145
+ precondition
146
+
147
+ save_token_if_it_changes do
148
+ super
113
149
  end
114
150
  end
115
151
  end
116
152
  end
117
153
 
118
- desc "target [URL]", "Set or display the current target cloud"
119
- def target(url = nil)
120
- if url.nil?
121
- display_target
122
- return
123
- end
154
+ def save_token_if_it_changes
155
+ return yield unless client && client.token
124
156
 
125
- target = sane_target_url(url)
126
- display = c(target.sub(/https?:\/\//, ""), :blue)
127
- with_progress("Setting target to #{display}") do
128
- unless force?
129
- # check that the target is valid
130
- CFoundry::Client.new(target).info
131
- end
157
+ before_token = client.token
158
+
159
+ yield
132
160
 
133
- set_target(target)
161
+ after_token = client.token
162
+
163
+ return unless after_token
164
+
165
+ if before_token != after_token
166
+ info = target_info
167
+ info[:token] = after_token.auth_header
168
+ save_target_info(info)
134
169
  end
135
170
  end
136
171
 
137
- desc "login [EMAIL]", "Authenticate with the target"
138
- flag(:email) {
139
- ask("Email")
140
- }
141
- flag(:password)
142
- # TODO: implement new authentication scheme
143
- def login(email = nil)
144
- unless simple_output?
145
- display_target
146
- puts ""
147
- end
172
+ def log_error(e)
173
+ ensure_config_dir
148
174
 
149
- email ||= input(:email)
150
- password = input(:password)
175
+ msg = e.class.name
176
+ msg << ": #{e}" unless e.to_s.empty?
151
177
 
152
- authenticated = false
153
- failed = false
154
- until authenticated
155
- unless force?
156
- if failed || !password
157
- password = ask("Password", :echo => "*", :forget => true)
158
- end
178
+ crash_file = File.expand_path(CF::CRASH_FILE)
179
+
180
+ FileUtils.mkdir_p(File.dirname(crash_file))
181
+
182
+ File.open(crash_file, "w") do |f|
183
+ f.puts "Time of crash:"
184
+ f.puts " #{Time.now}"
185
+ f.puts ""
186
+ f.puts msg
187
+ f.puts ""
188
+
189
+ if e.respond_to?(:request_trace)
190
+ f.puts "<<<"
191
+ f.puts e.request_trace
159
192
  end
160
193
 
161
- with_progress("Authenticating") do |s|
162
- begin
163
- save_token(client.login(email, password))
164
- authenticated = true
165
- rescue CFoundry::Denied
166
- return if force?
194
+ if e.respond_to?(:response_trace)
195
+ f.puts e.response_trace
196
+ f.puts ">>>"
197
+ f.puts ""
198
+ end
167
199
 
168
- s.fail do
169
- failed = true
170
- end
200
+ cf_dir = File.expand_path("../../../..", __FILE__) + "/"
201
+ e.backtrace.each do |loc|
202
+ if loc =~ /\/gems\//
203
+ f.puts loc.sub(/.*\/gems\//, "")
204
+ else
205
+ f.puts loc.sub(cf_dir, "")
171
206
  end
172
207
  end
173
208
  end
174
- ensure
175
- $exit_status = 1 if not authenticated
176
209
  end
177
210
 
178
- desc "logout", "Log out from the target"
179
- def logout
180
- with_progress("Logging out") do
181
- remove_token
182
- end
211
+ def quiet?
212
+ input[:quiet]
183
213
  end
184
214
 
185
- desc "register [EMAIL]", "Create a user and log in"
186
- flag(:email) {
187
- ask("Email")
188
- }
189
- flag(:password) {
190
- ask("Password", :echo => "*", :forget => true)
191
- }
192
- flag(:no_login, :type => :boolean)
193
- def register(email = nil)
194
- unless simple_output?
195
- puts "Target: #{c(client_target, :blue)}"
196
- puts ""
197
- end
215
+ def force?
216
+ input[:force]
217
+ end
218
+
219
+ def debug?
220
+ !!input[:debug]
221
+ end
222
+
223
+ def color_enabled?
224
+ input[:color]
225
+ end
226
+
227
+ def verbose?
228
+ input[:verbose]
229
+ end
230
+
231
+ def user_colors
232
+ return @user_colors if @user_colors
198
233
 
199
- email ||= input(:email)
200
- password = input(:password)
234
+ colors = File.expand_path(COLORS_FILE)
201
235
 
202
- with_progress("Creating user") do
203
- client.register(email, password)
236
+ @user_colors = super.dup
237
+
238
+ # most terminal schemes are stupid, so use cyan instead
239
+ @user_colors.each do |k, v|
240
+ if v == :blue
241
+ @user_colors[k] = :cyan
242
+ end
204
243
  end
205
244
 
206
- unless input(:skip_login)
207
- with_progress("Logging in") do
208
- save_token(client.login(email, password))
245
+ if File.exists?(colors)
246
+ YAML.load_file(colors).each do |k, v|
247
+ @user_colors[k.to_sym] = v.to_sym
209
248
  end
210
249
  end
250
+
251
+ @user_colors
252
+ end
253
+
254
+ def err(msg, status = 1)
255
+ $stderr.puts c(msg, :error)
256
+ exit_status status
257
+ end
258
+
259
+ def fail(msg)
260
+ raise UserError, msg
261
+ end
262
+
263
+ def table(headers, rows)
264
+ tabular(
265
+ !quiet? && headers.collect { |h| h && b(h) },
266
+ *rows)
267
+ end
268
+
269
+ def name_list(xs)
270
+ if xs.empty?
271
+ d("none")
272
+ else
273
+ xs.collect { |x| c(x.name, :name) }.join(", ")
274
+ end
211
275
  end
212
276
 
213
- desc "services", "List your services"
214
- def services
215
- services =
216
- with_progress("Getting services") do
217
- client.services
277
+ def sane_target_url(url)
278
+ unless url =~ /^https?:\/\//
279
+ begin
280
+ TCPSocket.new(url, Net::HTTP.https_default_port)
281
+ url = "https://#{url}"
282
+ rescue Errno::ECONNREFUSED, SocketError, Timeout::Error
283
+ url = "http://#{url}"
218
284
  end
285
+ end
286
+
287
+ url.gsub(/\/$/, "")
288
+ end
289
+
290
+ def target_file
291
+ one_of(CF::TARGET_FILE, CF::OLD_TARGET_FILE)
292
+ end
219
293
 
220
- puts "" unless simple_output?
294
+ def tokens_file
295
+ one_of(CF::TOKENS_FILE, CF::OLD_TOKENS_FILE)
296
+ end
221
297
 
222
- if services.empty? and !simple_output?
223
- puts "No services."
298
+ def one_of(*paths)
299
+ paths.each do |p|
300
+ exp = File.expand_path(p)
301
+ return exp if File.exist? exp
224
302
  end
225
303
 
226
- services.each do |s|
227
- display_service(s)
304
+ File.expand_path(paths.first)
305
+ end
306
+
307
+ def client_target
308
+ if File.exists?(target_file)
309
+ File.read(target_file).chomp
228
310
  end
229
311
  end
230
312
 
231
- desc "users", "List all users"
232
- def users
233
- users =
234
- with_progress("Getting users") do
235
- client.users
313
+ def ensure_config_dir
314
+ config = File.expand_path(CF::CONFIG_DIR)
315
+ FileUtils.mkdir_p(config) unless File.exist? config
316
+ end
317
+
318
+ def set_target(url)
319
+ ensure_config_dir
320
+
321
+ File.open(File.expand_path(CF::TARGET_FILE), "w") do |f|
322
+ f.write(sane_target_url(url))
323
+ end
324
+
325
+ invalidate_client
326
+ end
327
+
328
+ def targets_info
329
+ new_toks = File.expand_path(CF::TOKENS_FILE)
330
+ old_toks = File.expand_path(CF::OLD_TOKENS_FILE)
331
+
332
+ info =
333
+ if File.exist? new_toks
334
+ YAML.load_file(new_toks)
335
+ elsif File.exist? old_toks
336
+ MultiJson.load(File.read(old_toks))
236
337
  end
237
338
 
238
- users.each do |u|
239
- display_user(u)
339
+ info ||= {}
340
+
341
+ normalize_targets_info(info)
342
+ end
343
+
344
+ def normalize_targets_info(info_by_url)
345
+ info_by_url.reduce({}) do |hash, pair|
346
+ key, value = pair
347
+ hash[key] = value.is_a?(String) ? { :token => value } : value
348
+ hash
240
349
  end
241
350
  end
242
351
 
243
- private
352
+ def target_info(target = client_target)
353
+ targets_info[target] || {}
354
+ end
244
355
 
245
- def display_service(s)
246
- if simple_output?
247
- puts s.name
248
- else
249
- puts "#{c(s.name, :blue)}: #{s.vendor} v#{s.version}"
356
+ def save_targets(ts)
357
+ ensure_config_dir
358
+
359
+ File.open(File.expand_path(CF::TOKENS_FILE), "w") do |io|
360
+ YAML.dump(ts, io)
250
361
  end
251
362
  end
252
363
 
253
- def display_user(u)
254
- if simple_output?
255
- puts u.email
256
- else
257
- puts ""
258
- puts "#{c(u.email, :blue)}:"
259
- puts " admin?: #{c(u.admin?, u.admin? ? :green : :red)}"
364
+ def save_target_info(info, target = client_target)
365
+ ts = targets_info
366
+ ts[target] = info
367
+ save_targets(ts)
368
+ end
369
+
370
+ def remove_target_info(target = client_target)
371
+ ts = targets_info
372
+ ts.delete target
373
+ save_targets(ts)
374
+ end
375
+
376
+ def invalidate_client
377
+ @@client = nil
378
+ client
379
+ end
380
+
381
+ def client(target = client_target)
382
+ return @@client if defined?(@@client) && @@client
383
+ return unless target
384
+
385
+ info = target_info(target)
386
+ token = info[:token] && CFoundry::AuthToken.from_hash(info)
387
+
388
+ @@client =
389
+ case info[:version]
390
+ when 2
391
+ fail "User switching not implemented." if input[:proxy]
392
+ CFoundry::V2::Client.new(target, token)
393
+ when 1
394
+ CFoundry::V1::Client.new(target, token)
395
+ else
396
+ CFoundry::Client.new(target, token)
397
+ end
398
+
399
+ unless @@client.is_a?(CFoundry::V2::Client)
400
+ # TODO
401
+ fail "V1 targets are no longer supported."
402
+ end
403
+
404
+ @@client.proxy = input[:proxy]
405
+ @@client.trace = input[:trace]
406
+
407
+ uri = URI.parse(target)
408
+ @@client.log = File.expand_path("#{LOGS_DIR}/#{uri.host}.log")
409
+
410
+ unless info.key? :version
411
+ info[:version] = @@client.version
412
+ save_target_info(info, target)
413
+ end
414
+
415
+ if org = info[:organization]
416
+ @@client.current_organization = @@client.organization(org)
417
+ end
418
+
419
+ if space = info[:space]
420
+ @@client.current_space = @@client.space(space)
260
421
  end
422
+
423
+ @@client
424
+ rescue CFoundry::InvalidTarget
261
425
  end
262
426
 
263
- def display_target
264
- if simple_output?
265
- puts client.target
266
- else
267
- puts "Target: #{c(client.target, :blue)}"
427
+ def fail_unknown(display, name)
428
+ fail("Unknown #{display} '#{name}'.")
429
+ end
430
+
431
+ class << self
432
+ def client
433
+ @@client
434
+ end
435
+
436
+ def client=(c)
437
+ @@client = c
438
+ end
439
+
440
+ private
441
+
442
+ def find_by_name(display, &blk)
443
+ proc { |name, *args|
444
+ choices, _ = args
445
+ choices ||= instance_exec(&blk) if block_given?
446
+
447
+ choices.find { |c| c.name == name } ||
448
+ fail_unknown(display, name)
449
+ }
450
+ end
451
+
452
+ def by_name(what, display = what)
453
+ proc { |name, *_|
454
+ client.send(:"#{what}_by_name", name) ||
455
+ fail_unknown(display, name)
456
+ }
457
+ end
458
+
459
+ def find_by_name_insensitive(display, &blk)
460
+ proc { |name, *args|
461
+ choices, _ = args
462
+ choices ||= instance_exec(&blk) if block_given?
463
+
464
+ choices.find { |c| c.name.upcase == name.upcase } ||
465
+ fail_unknown(display, name)
466
+ }
268
467
  end
269
468
  end
270
469
  end