bosh_cli 1.2792.0 → 1.2797.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cli.rb CHANGED
@@ -46,6 +46,7 @@ require 'common/version/release_version_list'
46
46
  require 'common/version/bosh_version'
47
47
  require 'common/version/stemcell_version'
48
48
  require 'common/version/stemcell_version_list'
49
+ require 'common/thread_pool'
49
50
 
50
51
  require 'cli/config'
51
52
  require 'cli/core_ext'
@@ -62,6 +63,8 @@ require 'cli/director_task'
62
63
 
63
64
  require 'cli/line_wrap'
64
65
  require 'cli/backup_destination_path'
66
+ require 'cli/interactive_progress_renderer'
67
+ require 'cli/non_interactive_progress_renderer'
65
68
 
66
69
  require 'cli/source_control/git_ignore'
67
70
 
@@ -51,8 +51,12 @@ module Bosh::Cli
51
51
  @release = Bosh::Cli::Release.new(@work_dir, options[:final])
52
52
  end
53
53
 
54
+ def progress_renderer
55
+ interactive? ? Bosh::Cli::InteractiveProgressRenderer.new : Bosh::Cli::NonInteractiveProgressRenderer.new
56
+ end
57
+
54
58
  def blob_manager
55
- @blob_manager ||= Bosh::Cli::BlobManager.new(release)
59
+ @blob_manager ||= Bosh::Cli::BlobManager.new(release, config.max_parallel_downloads, progress_renderer)
56
60
  end
57
61
 
58
62
  def blobstore
@@ -1,4 +1,5 @@
1
1
  # Copyright (c) 2009-2012 VMware, Inc.
2
+ require 'logging'
2
3
 
3
4
  module Bosh::Cli
4
5
  # In order to avoid storing large objects in git repo,
@@ -10,7 +11,11 @@ module Bosh::Cli
10
11
  attr_reader :new_blobs, :updated_blobs
11
12
 
12
13
  # @param [Bosh::Cli::Release] release BOSH Release object
13
- def initialize(release)
14
+ def initialize(release, max_parallel_downloads, progress_renderer)
15
+ @progress_renderer = progress_renderer
16
+ max_parallel_downloads = 1 if max_parallel_downloads.nil? || max_parallel_downloads < 1
17
+ @max_parallel_downloads = max_parallel_downloads
18
+
14
19
  @release = release
15
20
  @index_file = File.join(@release.dir, "config", DEFAULT_INDEX_NAME)
16
21
 
@@ -205,6 +210,7 @@ module Bosh::Cli
205
210
  # establishes symlinks in blobs directory to any files present in index.
206
211
  # @return [void]
207
212
  def process_index
213
+ missing_blobs = []
208
214
  @index.each_pair do |path, entry|
209
215
  if File.exists?(File.join(@src_dir, path))
210
216
  err("File `#{path}' is in both blob index and src directory.\n" +
@@ -219,15 +225,24 @@ module Bosh::Cli
219
225
  if checksum == entry["sha"]
220
226
  need_download = false
221
227
  else
222
- progress(path, "checksum mismatch, re-downloading...\n".make_red)
228
+ @progress_renderer.error(path, "checksum mismatch, re-downloading...")
223
229
  end
224
230
  end
225
231
 
226
232
  if need_download
227
- local_path = download_blob(path)
233
+ missing_blobs << [path, entry["sha"]]
234
+ else
235
+ install_blob(local_path, path, entry["sha"])
228
236
  end
237
+ end
229
238
 
230
- install_blob(local_path, path, entry["sha"])
239
+ Bosh::ThreadPool.new(:max_threads => @max_parallel_downloads, :logger => Logging::Logger.new(nil)).wrap do |pool|
240
+ missing_blobs.each do |path, sha|
241
+ pool.process do
242
+ local_path = download_blob(path)
243
+ install_blob(local_path, path, sha)
244
+ end
245
+ end
231
246
  end
232
247
  end
233
248
 
@@ -250,9 +265,9 @@ module Bosh::Cli
250
265
 
251
266
  checksum = file_checksum(blob_path)
252
267
 
253
- progress(path, "uploading...")
268
+ @progress_renderer.start(path, "uploading...")
254
269
  object_id = @blobstore.create(File.open(blob_path, "r"))
255
- progress(path, "uploaded\n".make_green)
270
+ @progress_renderer.finish(path, "uploaded")
256
271
 
257
272
  @index[path] = {
258
273
  "object_id" => object_id,
@@ -278,7 +293,8 @@ module Bosh::Cli
278
293
 
279
294
  blob = @index[path]
280
295
  size = blob["size"].to_i
281
- tmp_file = File.open(File.join(Dir.mktmpdir, "bosh-blob"), "w")
296
+ blob_path = path.gsub(File::SEPARATOR, '-')
297
+ tmp_file = File.open(File.join(Dir.mktmpdir, blob_path), "w")
282
298
 
283
299
  download_label = "downloading"
284
300
  if size > 0
@@ -286,21 +302,22 @@ module Bosh::Cli
286
302
  end
287
303
 
288
304
  progress_bar = Thread.new do
305
+ @progress_renderer.start(path, "#{download_label}")
289
306
  loop do
290
307
  break unless size > 0
291
308
  if File.exists?(tmp_file.path)
292
309
  pct = 100 * File.size(tmp_file.path).to_f / size
293
- progress(path, "#{download_label} (#{pct.to_i}%)...")
310
+ @progress_renderer.progress(path, "#{download_label}", pct.to_i)
294
311
  end
295
312
  sleep(0.2)
296
313
  end
297
314
  end
298
315
 
299
- progress(path, "#{download_label}...")
316
+ @progress_renderer.progress(path, "#{download_label}", 100)
300
317
  @blobstore.get(blob["object_id"], tmp_file)
301
318
  tmp_file.close
302
319
  progress_bar.kill
303
- progress(path, "downloaded\n".make_green)
320
+ @progress_renderer.finish(path, "downloaded")
304
321
 
305
322
  if file_checksum(tmp_file.path) != blob["sha"]
306
323
  err("Checksum mismatch for downloaded blob `#{path}'")
@@ -311,15 +328,6 @@ module Bosh::Cli
311
328
 
312
329
  private
313
330
 
314
- # Renders blob operation progress
315
- # @param [String] path Blob path relative to blobs dir
316
- # @param [String] label Operation happening to a blob
317
- def progress(path, label)
318
- say("\r", " " * 80)
319
- say("\r#{path.truncate(40).make_yellow} #{label}", "")
320
- Bosh::Cli::Config.output.flush # Ruby 1.8 compatibility
321
- end
322
-
323
331
  # @param [String] src Path to a file containing the blob
324
332
  # @param [String] dst Resulting blob path relative to blobs dir
325
333
  # @param [String] checksum Blob checksum
@@ -379,3 +387,4 @@ module Bosh::Cli
379
387
  end
380
388
  end
381
389
  end
390
+
@@ -693,6 +693,8 @@ module Bosh
693
693
  rescue URI::Error,
694
694
  SocketError,
695
695
  Errno::ECONNREFUSED,
696
+ Errno::ETIMEDOUT,
697
+ Errno::ECONNRESET,
696
698
  Timeout::Error,
697
699
  HTTPClient::TimeoutError,
698
700
  HTTPClient::KeepAliveDisconnected,
@@ -55,7 +55,7 @@ module Bosh::Cli::Command
55
55
  t.headings = 'Name', 'Versions', 'Commit Hash'
56
56
  t.headings << 'Jobs' if show_jobs
57
57
  releases.each do |release|
58
- versions, commit_hashes = formatted_versions(release).transpose
58
+ versions, commit_hashes = formatted_versions(release['release_versions']).transpose
59
59
  row = [release['name'], versions.join("\n"), commit_hashes.join("\n")]
60
60
  if show_jobs
61
61
  jobs = formatted_jobs(release).transpose
@@ -66,8 +66,12 @@ module Bosh::Cli::Command
66
66
  end
67
67
  end
68
68
 
69
- def formatted_versions(release)
70
- sort_versions(release['release_versions']).map { |v| formatted_version_and_commit_hash(v) }
69
+ def formatted_versions(release_versions)
70
+ if release_versions.empty?
71
+ [["unknown", "unknown"]]
72
+ else
73
+ sort_versions(release_versions).map { |v| formatted_version_and_commit_hash(v) }
74
+ end
71
75
  end
72
76
 
73
77
  def sort_versions(versions)
@@ -19,6 +19,9 @@ module Bosh::Cli
19
19
 
20
20
  # @return [Integer] CLI polling interval
21
21
  attr_accessor :poll_interval
22
+
23
+ # @return [Integer] CLI max parallel downloads
24
+ attr_accessor :max_parallel_downloads
22
25
  end
23
26
 
24
27
  @commands = {}
@@ -166,6 +169,13 @@ module Bosh::Cli
166
169
  end
167
170
  end
168
171
 
172
+ # Read the max parallel downloads configuration.
173
+ #
174
+ # @return [Integer] The maximum number of parallel downloads
175
+ def max_parallel_downloads
176
+ self.class.max_parallel_downloads || @config_file.fetch("max_parallel_downloads", 1)
177
+ end
178
+
169
179
  def read(attr, try_local_first = true)
170
180
  attr = attr.to_s
171
181
  if try_local_first && @config_file[@work_dir].is_a?(Hash) &&
@@ -0,0 +1,70 @@
1
+ module Bosh::Cli
2
+ class InteractiveProgressRenderer
3
+ def initialize
4
+ @mutex = Mutex.new
5
+ @indices = {}
6
+ end
7
+
8
+ def start(path, label)
9
+ render(path, label)
10
+ end
11
+
12
+ def progress(path, label, percent)
13
+ render(path, "#{label} (#{percent}%)")
14
+ end
15
+
16
+ def error(path, message)
17
+ render(path, message.make_red)
18
+ end
19
+
20
+ def finish(path, label)
21
+ render(path, label.make_green)
22
+ end
23
+
24
+ private
25
+
26
+ def render(path, label)
27
+ @mutex.synchronize do
28
+ truncated_path = path.truncate(40)
29
+ truncated_path_length = truncated_path.length + 1
30
+
31
+ if !@indices.has_key?(path)
32
+ say(truncated_path.make_yellow, " \n")
33
+ end
34
+
35
+ @indices[path] ||= @indices.count
36
+
37
+ save_cursor_position
38
+ up(@indices.count - @indices[path])
39
+
40
+ right(truncated_path_length)
41
+ clear_line
42
+
43
+ say(label, "")
44
+
45
+ restore_cursor_position
46
+ Bosh::Cli::Config.output.flush # Ruby 1.8 compatibility
47
+ end
48
+ end
49
+
50
+ def save_cursor_position
51
+ say("\033[s", "")
52
+ end
53
+
54
+ def restore_cursor_position
55
+ say("\033[u", "")
56
+ end
57
+
58
+ def up(count = 1)
59
+ say("\033[#{count}A", "")
60
+ end
61
+
62
+ def right(count = 1)
63
+ say("\033[#{count}C", "")
64
+ end
65
+
66
+ def clear_line
67
+ say("\033[K", "")
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,32 @@
1
+ module Bosh::Cli
2
+ class NonInteractiveProgressRenderer
3
+ def initialize
4
+ @mutex = Mutex.new
5
+ end
6
+
7
+ def start(path, label)
8
+ render(path, label)
9
+ end
10
+
11
+ def progress(path, label, percent)
12
+ end
13
+
14
+ def error(path, message)
15
+ render(path, message)
16
+ end
17
+
18
+ def finish(path, label)
19
+ render(path, label)
20
+ end
21
+
22
+ private
23
+
24
+ def render(path, label)
25
+ @mutex.synchronize do
26
+ truncated_path = path.truncate(40)
27
+ say("#{truncated_path} #{label}")
28
+ Bosh::Cli::Config.output.flush # Ruby 1.8 compatibility
29
+ end
30
+ end
31
+ end
32
+ end
@@ -114,6 +114,10 @@ module Bosh::Cli
114
114
  @options[:config] = file
115
115
  end
116
116
 
117
+ opts.on("--parallel MAX", "Sets the max number of parallel downloads") do |max|
118
+ Config.max_parallel_downloads = Integer(max)
119
+ end
120
+
117
121
  opts.on("--[no-]color", "Toggle colorized output") do |v|
118
122
  Config.colorize = v
119
123
  end
@@ -1,5 +1,5 @@
1
1
  module Bosh
2
2
  module Cli
3
- VERSION = '1.2792.0'
3
+ VERSION = '1.2797.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bosh_cli
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2792.0
4
+ version: 1.2797.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-12-16 00:00:00.000000000 Z
12
+ date: 2014-12-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bosh_common
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 1.2792.0
21
+ version: 1.2797.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 1.2792.0
29
+ version: 1.2797.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: bosh-template
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -34,7 +34,7 @@ dependencies:
34
34
  requirements:
35
35
  - - ~>
36
36
  - !ruby/object:Gem::Version
37
- version: 1.2792.0
37
+ version: 1.2797.0
38
38
  type: :runtime
39
39
  prerelease: false
40
40
  version_requirements: !ruby/object:Gem::Requirement
@@ -42,7 +42,7 @@ dependencies:
42
42
  requirements:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
- version: 1.2792.0
45
+ version: 1.2797.0
46
46
  - !ruby/object:Gem::Dependency
47
47
  name: json_pure
48
48
  requirement: !ruby/object:Gem::Requirement
@@ -130,7 +130,7 @@ dependencies:
130
130
  requirements:
131
131
  - - ~>
132
132
  - !ruby/object:Gem::Version
133
- version: 1.2792.0
133
+ version: 1.2797.0
134
134
  type: :runtime
135
135
  prerelease: false
136
136
  version_requirements: !ruby/object:Gem::Requirement
@@ -138,7 +138,7 @@ dependencies:
138
138
  requirements:
139
139
  - - ~>
140
140
  - !ruby/object:Gem::Version
141
- version: 1.2792.0
141
+ version: 1.2797.0
142
142
  - !ruby/object:Gem::Dependency
143
143
  name: net-ssh
144
144
  requirement: !ruby/object:Gem::Requirement
@@ -221,7 +221,7 @@ dependencies:
221
221
  version: 0.5.4
222
222
  description: ! 'BOSH CLI
223
223
 
224
- 5e7f33'
224
+ 3fe6aa'
225
225
  email: support@cloudfoundry.com
226
226
  executables:
227
227
  - bosh
@@ -282,6 +282,7 @@ files:
282
282
  - lib/cli/download_with_progress.rb
283
283
  - lib/cli/errors.rb
284
284
  - lib/cli/file_with_progress_bar.rb
285
+ - lib/cli/interactive_progress_renderer.rb
285
286
  - lib/cli/job_builder.rb
286
287
  - lib/cli/job_command_args.rb
287
288
  - lib/cli/job_property_collection.rb
@@ -291,6 +292,7 @@ files:
291
292
  - lib/cli/logs_downloader.rb
292
293
  - lib/cli/manifest_warnings.rb
293
294
  - lib/cli/name_version_pair.rb
295
+ - lib/cli/non_interactive_progress_renderer.rb
294
296
  - lib/cli/package_builder.rb
295
297
  - lib/cli/packaging_helper.rb
296
298
  - lib/cli/public_stemcell.rb
@@ -344,7 +346,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
344
346
  version: '0'
345
347
  segments:
346
348
  - 0
347
- hash: -1339837986300098617
349
+ hash: -2793466830286606539
348
350
  requirements: []
349
351
  rubyforge_project:
350
352
  rubygems_version: 1.8.23