cf 0.1.5 → 0.6.0.rc1

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