bosh_cli 1.2792.0 → 1.2797.0

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.
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