vmc 0.4.0.beta.93 → 0.4.0.beta.94

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/vmc-ng/Rakefile +21 -30
  2. data/vmc-ng/lib/vmc.rb +4 -3
  3. data/vmc-ng/lib/vmc/cli.rb +10 -9
  4. data/vmc-ng/lib/vmc/cli/app/app.rb +45 -0
  5. data/vmc-ng/lib/vmc/cli/app/apps.rb +97 -0
  6. data/vmc-ng/lib/vmc/cli/app/base.rb +82 -0
  7. data/vmc-ng/lib/vmc/cli/app/crashes.rb +41 -0
  8. data/vmc-ng/lib/vmc/cli/app/delete.rb +90 -0
  9. data/vmc-ng/lib/vmc/cli/app/deprecated.rb +11 -0
  10. data/vmc-ng/lib/vmc/cli/app/env.rb +86 -0
  11. data/vmc-ng/lib/vmc/cli/app/files.rb +85 -0
  12. data/vmc-ng/lib/vmc/cli/app/health.rb +27 -0
  13. data/vmc-ng/lib/vmc/cli/app/instances.rb +49 -0
  14. data/vmc-ng/lib/vmc/cli/app/logs.rb +80 -0
  15. data/vmc-ng/lib/vmc/cli/app/push.rb +336 -0
  16. data/vmc-ng/lib/vmc/cli/app/rename.rb +31 -0
  17. data/vmc-ng/lib/vmc/cli/app/restart.rb +23 -0
  18. data/vmc-ng/lib/vmc/cli/app/routes.rb +97 -0
  19. data/vmc-ng/lib/vmc/cli/app/scale.rb +67 -0
  20. data/vmc-ng/lib/vmc/cli/app/start.rb +96 -0
  21. data/vmc-ng/lib/vmc/cli/app/stats.rb +68 -0
  22. data/vmc-ng/lib/vmc/cli/app/stop.rb +29 -0
  23. data/vmc-ng/lib/vmc/cli/domain/add_domain.rb +27 -0
  24. data/vmc-ng/lib/vmc/cli/domain/base.rb +12 -0
  25. data/vmc-ng/lib/vmc/cli/domain/create_domain.rb +31 -0
  26. data/vmc-ng/lib/vmc/cli/domain/delete_domain.rb +51 -0
  27. data/vmc-ng/lib/vmc/cli/domain/domains.rb +43 -0
  28. data/vmc-ng/lib/vmc/cli/domain/remove_domain.rb +26 -0
  29. data/vmc-ng/lib/vmc/cli/help.rb +0 -1
  30. data/vmc-ng/lib/vmc/cli/interactive.rb +4 -0
  31. data/vmc-ng/lib/vmc/cli/route/base.rb +12 -0
  32. data/vmc-ng/lib/vmc/cli/route/create_route.rb +42 -0
  33. data/vmc-ng/lib/vmc/cli/route/delete_route.rb +42 -0
  34. data/vmc-ng/lib/vmc/cli/route/routes.rb +26 -0
  35. data/vmc-ng/lib/vmc/detect.rb +2 -2
  36. data/vmc-ng/lib/vmc/spec_helper.rb +1 -0
  37. data/vmc-ng/lib/vmc/version.rb +1 -1
  38. data/vmc-ng/spec/cli/app/push_spec.rb +34 -0
  39. data/vmc-ng/spec/cli/app/rename_spec.rb +108 -0
  40. data/vmc-ng/spec/cli/route/delete_route_spec.rb +160 -0
  41. data/vmc-ng/spec/detect_spec.rb +54 -0
  42. data/vmc-ng/spec/factories/app_factory.rb +9 -0
  43. data/vmc-ng/spec/factories/client_factory.rb +16 -0
  44. data/vmc-ng/spec/factories/domain_factory.rb +9 -0
  45. data/vmc-ng/spec/factories/factory.rb +3 -0
  46. data/vmc-ng/spec/factories/framework_factory.rb +9 -0
  47. data/vmc-ng/spec/factories/route_factory.rb +10 -0
  48. data/vmc-ng/spec/spec_helper.rb +17 -0
  49. data/vmc-ng/spec/support/interact_helpers.rb +23 -0
  50. metadata +135 -62
  51. data/vmc-ng/lib/vmc/cli/app.rb +0 -1333
  52. data/vmc-ng/lib/vmc/cli/domain.rb +0 -164
  53. data/vmc-ng/lib/vmc/cli/route.rb +0 -106
  54. data/vmc-ng/lib/vmc/spec_helpers.rb +0 -431
  55. data/vmc-ng/lib/vmc/spec_helpers/eventlog.rb +0 -277
  56. data/vmc-ng/lib/vmc/spec_helpers/patches.rb +0 -94
  57. data/vmc-ng/spec/Rakefile +0 -13
  58. data/vmc-ng/spec/app/app_spec.rb +0 -19
  59. data/vmc-ng/spec/app/apps_spec.rb +0 -79
  60. data/vmc-ng/spec/app/push_spec.rb +0 -74
  61. data/vmc-ng/spec/assets/hello-sinatra/Gemfile +0 -2
  62. data/vmc-ng/spec/assets/hello-sinatra/main.rb +0 -5
  63. data/vmc-ng/spec/assets/hello-sinatra/manifest.yml +0 -9
  64. data/vmc-ng/spec/helpers.rb +0 -7
  65. data/vmc-ng/spec/start/target_spec.rb +0 -60
@@ -0,0 +1,11 @@
1
+ require "vmc/cli/app/base"
2
+
3
+ module VMC::App
4
+ class Deprecated < Base
5
+ desc "DEPRECATED. Use 'push' instead."
6
+ input :app, :argument => :optional
7
+ def update
8
+ fail "The 'update' command is no longer needed; use 'push' instead."
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,86 @@
1
+ require "vmc/cli/app/base"
2
+
3
+ module VMC::App
4
+ class Env < Base
5
+ VALID_ENV_VAR = /^[a-zA-Za-z_][[:alnum:]_]*$/
6
+
7
+ desc "Show all environment variables set for an app"
8
+ group :apps, :info, :hidden => true
9
+ input :app, :argument => true,
10
+ :desc => "Application to inspect the environment of",
11
+ :from_given => by_name("app")
12
+ def env
13
+ app = input[:app]
14
+
15
+ vars =
16
+ with_progress("Getting env for #{c(app.name, :name)}") do |s|
17
+ app.env
18
+ end
19
+
20
+ line unless quiet?
21
+
22
+ vars.each do |name, val|
23
+ line "#{c(name, :name)}: #{val}"
24
+ end
25
+ end
26
+
27
+ desc "Set an environment variable"
28
+ group :apps, :info, :hidden => true
29
+ input :app, :argument => true,
30
+ :desc => "Application to set the variable for",
31
+ :from_given => by_name("app")
32
+ input :name, :argument => true,
33
+ :desc => "Environment variable name"
34
+ input :value, :argument => :optional,
35
+ :desc => "Environment variable value"
36
+ input :restart, :type => :boolean, :default => true,
37
+ :desc => "Restart app after updating?"
38
+ def set_env
39
+ app = input[:app]
40
+ name = input[:name]
41
+
42
+ if value = input[:value]
43
+ name = input[:name]
44
+ elsif name["="]
45
+ name, value = name.split("=")
46
+ end
47
+
48
+ unless name =~ VALID_ENV_VAR
49
+ fail "Invalid variable name; must match #{VALID_ENV_VAR.inspect}"
50
+ end
51
+
52
+ with_progress("Updating #{c(app.name, :name)}") do
53
+ app.env[name] = value
54
+ app.update!
55
+ end
56
+
57
+ if app.started? && input[:restart]
58
+ invoke :restart, :app => app
59
+ end
60
+ end
61
+
62
+
63
+ desc "Remove an environment variable"
64
+ group :apps, :info, :hidden => true
65
+ input :app, :argument => true,
66
+ :desc => "Application to set the variable for",
67
+ :from_given => by_name("app")
68
+ input :name, :argument => true,
69
+ :desc => "Environment variable name"
70
+ input :restart, :type => :boolean, :default => true,
71
+ :desc => "Restart app after updating?"
72
+ def unset_env
73
+ app = input[:app]
74
+ name = input[:name]
75
+
76
+ with_progress("Updating #{c(app.name, :name)}") do
77
+ app.env.delete(name)
78
+ app.update!
79
+ end
80
+
81
+ if app.started? && input[:restart]
82
+ invoke :restart, :app => app
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,85 @@
1
+ require "vmc/cli/app/base"
2
+
3
+ module VMC::App
4
+ class Files < Base
5
+ desc "Print out an app's file contents"
6
+ group :apps, :info, :hidden => true
7
+ input :app, :argument => true,
8
+ :desc => "Application to inspect the files of",
9
+ :from_given => by_name("app")
10
+ input :path, :argument => true, :default => "/",
11
+ :desc => "Path of file to read"
12
+ def file
13
+ app = input[:app]
14
+ path = input[:path]
15
+
16
+ file =
17
+ with_progress("Getting file contents") do
18
+ app.file(*path.split("/"))
19
+ end
20
+
21
+ if quiet?
22
+ print file
23
+ else
24
+ line
25
+
26
+ file.split("\n").each do |l|
27
+ line l
28
+ end
29
+ end
30
+ rescue CFoundry::NotFound
31
+ fail "Invalid path #{b(path)} for app #{b(app.name)}"
32
+ rescue CFoundry::FileError => e
33
+ fail e.description
34
+ end
35
+
36
+ desc "Examine an app's files"
37
+ group :apps, :info, :hidden => true
38
+ input :app, :argument => true,
39
+ :desc => "Application to inspect the files of",
40
+ :from_given => by_name("app")
41
+ input :path, :argument => :optional, :default => "/",
42
+ :desc => "Path of directory to list"
43
+ def files
44
+ app = input[:app]
45
+ path = input[:path]
46
+
47
+ if quiet?
48
+ files =
49
+ with_progress("Getting file listing") do
50
+ app.files(*path.split("/"))
51
+ end
52
+
53
+ files.each do |file|
54
+ line file.join("/")
55
+ end
56
+ else
57
+ invoke :file, :app => app, :path => path
58
+ end
59
+ rescue CFoundry::NotFound
60
+ fail "Invalid path #{b(path)} for app #{b(app.name)}"
61
+ rescue CFoundry::FileError => e
62
+ fail e.description
63
+ end
64
+
65
+ desc "Stream an app's file contents"
66
+ group :apps, :info, :hidden => true
67
+ input :app, :argument => true,
68
+ :desc => "Application to inspect the file of",
69
+ :from_given => by_name("app")
70
+ input :path, :argument => true, :default => "/",
71
+ :desc => "Path of file to stream"
72
+ def tail
73
+ app = input[:app]
74
+ path = input[:path]
75
+
76
+ app.stream_file(*path.split("/")) do |contents|
77
+ print contents
78
+ end
79
+ rescue CFoundry::NotFound
80
+ fail "Invalid path #{b(path)} for app #{b(app.name)}"
81
+ rescue CFoundry::FileError => e
82
+ fail e.description
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,27 @@
1
+ require "vmc/cli/app/base"
2
+
3
+ module VMC::App
4
+ class Health < Base
5
+ desc "Get application health"
6
+ group :apps, :info, :hidden => true
7
+ input :apps, :argument => :splat, :singular => :app,
8
+ :desc => "Applications to start",
9
+ :from_given => by_name("app")
10
+ def health
11
+ apps = input[:apps]
12
+ fail "No applications given." if apps.empty?
13
+
14
+ health =
15
+ with_progress("Getting health status") do
16
+ apps.collect { |a| [a, app_status(a)] }
17
+ end
18
+
19
+ line unless quiet?
20
+
21
+ spaced(health) do |app, status|
22
+ start_line "#{c(app.name, :name)}: " unless quiet?
23
+ puts status
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ require "vmc/cli/app/base"
2
+
3
+ module VMC::App
4
+ class Instances < Base
5
+ desc "List an app's instances"
6
+ group :apps, :info, :hidden => true
7
+ input :apps, :argument => :splat, :singular => :app,
8
+ :desc => "Applications whose instances to list",
9
+ :from_given => by_name("app")
10
+ def instances
11
+ apps = input[:apps]
12
+ fail "No applications given." if apps.empty?
13
+
14
+ spaced(apps) do |app|
15
+ instances =
16
+ with_progress("Getting instances for #{c(app.name, :name)}") do
17
+ app.instances
18
+ end
19
+
20
+ spaced(instances) do |i|
21
+ if quiet?
22
+ line i.id
23
+ else
24
+ display_instance(i)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def display_instance(i)
31
+ start_line "instance #{c("\##{i.id}", :instance)}: "
32
+ puts "#{b(c(i.state.downcase, state_color(i.state)))} "
33
+
34
+ indented do
35
+ if s = i.since
36
+ line "started: #{c(s.strftime("%F %r"), :neutral)}"
37
+ end
38
+
39
+ if d = i.debugger
40
+ line "debugger: port #{b(d[:port])} at #{b(d[:ip])}"
41
+ end
42
+
43
+ if c = i.console
44
+ line "console: port #{b(c[:port])} at #{b(c[:ip])}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,80 @@
1
+ require "vmc/cli/app/base"
2
+
3
+ module VMC::App
4
+ class Logs < Base
5
+ desc "Print out an app's logs"
6
+ group :apps, :info, :hidden => true
7
+ input :app, :argument => true,
8
+ :desc => "Application to get the logs of",
9
+ :from_given => by_name("app")
10
+ input :instance, :default => "0",
11
+ :desc => "Instance of application to get the logs of"
12
+ input :all, :type => :boolean, :default => false,
13
+ :desc => "Get logs for every instance"
14
+ def logs
15
+ app = input[:app]
16
+
17
+ instances =
18
+ if input[:all] || input[:instance] == "all"
19
+ app.instances
20
+ else
21
+ app.instances.select { |i| i.id == input[:instance] }
22
+ end
23
+
24
+ if instances.empty?
25
+ if input[:all]
26
+ fail "No instances found."
27
+ else
28
+ fail "Instance #{app.name} \##{input[:instance]} not found."
29
+ end
30
+ end
31
+
32
+ spaced(instances) do |i|
33
+ show_instance_logs(app, i)
34
+ end
35
+ end
36
+
37
+ desc "Print out the logs for an app's crashed instances"
38
+ group :apps, :info, :hidden => true
39
+ input :app, :argument => true,
40
+ :desc => "Application to get the logs of",
41
+ :from_given => by_name("app")
42
+ def crashlogs
43
+ app = input[:app]
44
+
45
+ crashes = app.crashes
46
+
47
+ fail "No crashed instances found." if crashes.empty?
48
+
49
+ spaced(crashes) do |i|
50
+ show_instance_logs(app, i)
51
+ end
52
+ end
53
+
54
+ def show_instance_logs(app, i)
55
+ return unless i.id
56
+
57
+ logs =
58
+ with_progress(
59
+ "Getting logs for #{c(app.name, :name)} " +
60
+ c("\##{i.id}", :instance)) do
61
+ i.files("logs")
62
+ end
63
+
64
+ line unless quiet?
65
+
66
+ spaced(logs) do |log|
67
+ begin
68
+ body =
69
+ with_progress("Reading " + b(log.join("/"))) do |s|
70
+ i.file(*log)
71
+ end
72
+
73
+ lines body
74
+ line unless body.empty?
75
+ rescue CFoundry::NotFound
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,336 @@
1
+ require "vmc/detect"
2
+
3
+ require "vmc/cli/app/base"
4
+
5
+ module VMC::App
6
+ class Push < Base
7
+ desc "Push an application, syncing changes if it exists"
8
+ group :apps, :manage
9
+ input(:name, :argument => true, :desc => "Application name") {
10
+ ask("Name")
11
+ }
12
+ input :path, :default => ".",
13
+ :desc => "Path containing the application"
14
+ input(:url, :desc => "URL bound to app") { |name|
15
+ choices = url_choices(name)
16
+
17
+ options = {
18
+ :choices => choices + ["none"],
19
+ :allow_other => true
20
+ }
21
+
22
+ options[:default] = choices.first if choices.size == 1
23
+
24
+ url = ask "URL", options
25
+
26
+ unless url == "none"
27
+ url
28
+ end
29
+ }
30
+ input(:memory, :desc => "Memory limit") { |default|
31
+ ask("Memory Limit",
32
+ :choices => memory_choices,
33
+ :allow_other => true,
34
+ :default => default || "64M")
35
+ }
36
+ input(:instances, :type => :integer,
37
+ :desc => "Number of instances to run") {
38
+ ask("Instances", :default => 1)
39
+ }
40
+ input(:framework, :from_given => find_by_name("framework"),
41
+ :desc => "Framework to use") { |all, choices, default, other|
42
+ ask_with_other("Framework", all, choices, default, other)
43
+ }
44
+ input(:runtime, :from_given => find_by_name("runtime"),
45
+ :desc => "Runtime to use") { |all, choices, default, other|
46
+ ask_with_other("Runtime", all, choices, default, other)
47
+ }
48
+ input(:command, :desc => "Startup command for standalone app") {
49
+ ask("Startup command")
50
+ }
51
+ input :plan, :default => "D100",
52
+ :desc => "Application plan (e.g. D100, P200)"
53
+ input :start, :type => :boolean, :default => true,
54
+ :desc => "Start app after pushing?"
55
+ input :restart, :type => :boolean, :default => true,
56
+ :desc => "Restart app after updating?"
57
+ input(:create_services, :type => :boolean,
58
+ :desc => "Interactively create services?") {
59
+ line unless quiet?
60
+ ask "Create services for application?", :default => false
61
+ }
62
+ input(:bind_services, :type => :boolean,
63
+ :desc => "Interactively bind services?") {
64
+ ask "Bind other services to application?", :default => false
65
+ }
66
+ def push
67
+ name = input[:name]
68
+ path = File.expand_path(input[:path])
69
+
70
+ if app = client.app_by_name(name)
71
+ sync_app(app, path)
72
+ else
73
+ create_app(name, path)
74
+ end
75
+ end
76
+
77
+ def sync_app(app, path)
78
+ upload_app(app, path)
79
+
80
+ diff = {}
81
+
82
+ if input.given?(:memory)
83
+ mem = megabytes(input[:memory])
84
+
85
+ if mem != app.memory
86
+ diff[:memory] = [app.memory, mem]
87
+ app.memory = mem
88
+ end
89
+ end
90
+
91
+ if input.given?(:instances)
92
+ instances = input[:instances]
93
+
94
+ if instances != app.total_instances
95
+ diff[:instances] = [app.total_instances, instances]
96
+ app.total_instances = instances
97
+ end
98
+ end
99
+
100
+ if input.given?(:framework)
101
+ all_frameworks = client.frameworks
102
+
103
+ framework = input[:framework, all_frameworks, all_frameworks]
104
+
105
+ if framework != app.framework
106
+ diff[:framework] = [app.framework.name, framework.name]
107
+ app.framework = framework
108
+ end
109
+ end
110
+
111
+ if input.given?(:runtime)
112
+ all_runtimes = client.runtimes
113
+
114
+ runtime = input[:runtime, all_runtimes, all_runtimes]
115
+
116
+ if runtime != app.runtime
117
+ diff[:runtime] = [app.runtime.name, runtime.name]
118
+ app.runtime = runtime
119
+ end
120
+ end
121
+
122
+ if input.given?(:command) && input[:command] != app.command
123
+ command = input[:command]
124
+
125
+ if command != app.command
126
+ diff[:command] = [app.command, command]
127
+ app.command = command
128
+ end
129
+ end
130
+
131
+ if input.given?(:plan) && v2?
132
+ production = !!(input[:plan] =~ /^p/i)
133
+
134
+ if production != app.production
135
+ diff[:production] = [bool(app.production), bool(production)]
136
+ app.production = production
137
+ end
138
+ end
139
+
140
+ unless diff.empty?
141
+ line "Changes:"
142
+
143
+ indented do
144
+ diff.each do |name, change|
145
+ old, new = change
146
+ line "#{c(name, :name)}: #{old} #{c("->", :dim)} #{new}"
147
+ end
148
+ end
149
+
150
+ with_progress("Updating #{c(app.name, :name)}") do
151
+ app.update!
152
+ end
153
+ end
154
+
155
+ if input[:restart] && app.started?
156
+ invoke :restart, :app => app
157
+ end
158
+ end
159
+
160
+ def create_app(name, path)
161
+ app = client.app
162
+ app.name = name
163
+ app.space = client.current_space if client.current_space
164
+ app.total_instances = input[:instances]
165
+ app.production = !!(input[:plan] =~ /^p/i) if v2?
166
+
167
+ detector = VMC::Detector.new(client, path)
168
+ all_frameworks = detector.all_frameworks
169
+ all_runtimes = detector.all_runtimes
170
+
171
+ if detected_framework = detector.detect_framework
172
+ framework = input[
173
+ :framework,
174
+ all_frameworks,
175
+ [detected_framework],
176
+ detected_framework,
177
+ :other
178
+ ]
179
+ else
180
+ framework = input[:framework, all_frameworks, all_frameworks]
181
+ end
182
+
183
+
184
+ if framework.name == "standalone"
185
+ detected_runtimes = detector.detect_runtimes
186
+ else
187
+ detected_runtimes = detector.runtimes(framework)
188
+ end
189
+
190
+ if detected_runtimes.size == 1
191
+ default_runtime = detected_runtimes.first
192
+ end
193
+
194
+ if detected_runtimes.empty?
195
+ runtime = input[:runtime, all_runtimes, all_runtimes]
196
+ else
197
+ runtime = input[
198
+ :runtime,
199
+ all_runtimes,
200
+ detected_runtimes,
201
+ default_runtime,
202
+ :other
203
+ ]
204
+ end
205
+
206
+
207
+ fail "Invalid framework '#{input[:framework]}'" unless framework
208
+ fail "Invalid runtime '#{input[:runtime]}'" unless runtime
209
+
210
+ app.framework = framework
211
+ app.runtime = runtime
212
+
213
+ app.command = input[:command] if framework.name == "standalone"
214
+
215
+ default_memory = detector.suggested_memory(framework) || 64
216
+ app.memory = megabytes(input[:memory, human_mb(default_memory)])
217
+
218
+ app = filter(:create_app, app)
219
+
220
+ with_progress("Creating #{c(app.name, :name)}") do
221
+ app.create!
222
+ end
223
+
224
+ line unless quiet?
225
+
226
+ url = input[:url, name]
227
+
228
+ mapped_url = false
229
+ until !url || mapped_url
230
+ begin
231
+ invoke :map, :app => app, :url => url
232
+ mapped_url = true
233
+ rescue CFoundry::RouteHostTaken, CFoundry::UriAlreadyTaken => e
234
+ line c(e.description, :bad)
235
+ line
236
+
237
+ input.forget(:url)
238
+ url = input[:url, name]
239
+
240
+ # version bumps on v1 even though mapping fails
241
+ app.invalidate! unless v2?
242
+ end
243
+ end
244
+
245
+ bindings = []
246
+
247
+ if input[:create_services] && !force?
248
+ while true
249
+ invoke :create_service, { :app => app }, :plan => :interact
250
+ break unless ask "Create another service?", :default => false
251
+ end
252
+ end
253
+
254
+ if input[:bind_services] && !force?
255
+ instances = client.service_instances
256
+
257
+ while true
258
+ invoke :bind_service, :app => app
259
+
260
+ break if (instances - app.services).empty?
261
+
262
+ break unless ask("Bind another service?", :default => false)
263
+ end
264
+ end
265
+
266
+ app = filter(:push_app, app)
267
+
268
+ begin
269
+ upload_app(app, path)
270
+ rescue
271
+ err "Upload failed. Try again with 'vmc push'."
272
+ raise
273
+ end
274
+
275
+ invoke :start, :app => app if input[:start]
276
+ end
277
+
278
+ private
279
+
280
+ def upload_app(app, path)
281
+ with_progress("Uploading #{c(app.name, :name)}") do
282
+ app.upload(path)
283
+ end
284
+ end
285
+
286
+ def bool(b)
287
+ if b
288
+ c("true", :yes)
289
+ else
290
+ c("false", :no)
291
+ end
292
+ end
293
+
294
+ def url_choices(name)
295
+ if v2?
296
+ client.current_space.domains.sort_by(&:name).collect do |d|
297
+ # TODO: check availability
298
+ "#{name}.#{d.name}"
299
+ end
300
+ else
301
+ ["#{name}.#{target_base}"]
302
+ end
303
+ end
304
+
305
+ def target_base
306
+ client.target.sub(/^https?:\/\/([^\.]+\.)?(.+)\/?/, '\2')
307
+ end
308
+
309
+ def ask_with_other(message, all, choices, default, other)
310
+ choices = choices.sort_by(&:name)
311
+ choices << other if other
312
+
313
+ opts = {
314
+ :choices => choices,
315
+ :display => proc { |x|
316
+ if other && x == other
317
+ "other"
318
+ else
319
+ x.name
320
+ end
321
+ }
322
+ }
323
+
324
+ opts[:default] = default if default
325
+
326
+ res = ask(message, opts)
327
+
328
+ if other && res == other
329
+ opts[:choices] = all
330
+ res = ask(message, opts)
331
+ end
332
+
333
+ res
334
+ end
335
+ end
336
+ end