bosh_cli 0.16

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 (113) hide show
  1. data/README +4 -0
  2. data/Rakefile +55 -0
  3. data/bin/bosh +17 -0
  4. data/lib/cli.rb +76 -0
  5. data/lib/cli/cache.rb +44 -0
  6. data/lib/cli/changeset_helper.rb +142 -0
  7. data/lib/cli/command_definition.rb +52 -0
  8. data/lib/cli/commands/base.rb +245 -0
  9. data/lib/cli/commands/biff.rb +300 -0
  10. data/lib/cli/commands/blob.rb +125 -0
  11. data/lib/cli/commands/cloudcheck.rb +169 -0
  12. data/lib/cli/commands/deployment.rb +147 -0
  13. data/lib/cli/commands/job.rb +42 -0
  14. data/lib/cli/commands/job_management.rb +117 -0
  15. data/lib/cli/commands/log_management.rb +81 -0
  16. data/lib/cli/commands/maintenance.rb +131 -0
  17. data/lib/cli/commands/misc.rb +240 -0
  18. data/lib/cli/commands/package.rb +112 -0
  19. data/lib/cli/commands/property_management.rb +125 -0
  20. data/lib/cli/commands/release.rb +469 -0
  21. data/lib/cli/commands/ssh.rb +271 -0
  22. data/lib/cli/commands/stemcell.rb +184 -0
  23. data/lib/cli/commands/task.rb +213 -0
  24. data/lib/cli/commands/user.rb +28 -0
  25. data/lib/cli/commands/vms.rb +53 -0
  26. data/lib/cli/config.rb +154 -0
  27. data/lib/cli/core_ext.rb +145 -0
  28. data/lib/cli/dependency_helper.rb +62 -0
  29. data/lib/cli/deployment_helper.rb +263 -0
  30. data/lib/cli/deployment_manifest_compiler.rb +28 -0
  31. data/lib/cli/director.rb +633 -0
  32. data/lib/cli/director_task.rb +64 -0
  33. data/lib/cli/errors.rb +48 -0
  34. data/lib/cli/event_log_renderer.rb +351 -0
  35. data/lib/cli/job_builder.rb +226 -0
  36. data/lib/cli/package_builder.rb +254 -0
  37. data/lib/cli/packaging_helper.rb +248 -0
  38. data/lib/cli/release.rb +176 -0
  39. data/lib/cli/release_builder.rb +215 -0
  40. data/lib/cli/release_compiler.rb +178 -0
  41. data/lib/cli/release_tarball.rb +272 -0
  42. data/lib/cli/runner.rb +771 -0
  43. data/lib/cli/stemcell.rb +83 -0
  44. data/lib/cli/task_log_renderer.rb +40 -0
  45. data/lib/cli/templates/help_message.erb +75 -0
  46. data/lib/cli/validation.rb +42 -0
  47. data/lib/cli/version.rb +7 -0
  48. data/lib/cli/version_calc.rb +48 -0
  49. data/lib/cli/versions_index.rb +126 -0
  50. data/lib/cli/yaml_helper.rb +62 -0
  51. data/spec/assets/biff/bad_gateway_config.yml +28 -0
  52. data/spec/assets/biff/good_simple_config.yml +63 -0
  53. data/spec/assets/biff/good_simple_golden_config.yml +63 -0
  54. data/spec/assets/biff/good_simple_template.erb +69 -0
  55. data/spec/assets/biff/multiple_subnets_config.yml +40 -0
  56. data/spec/assets/biff/network_only_template.erb +34 -0
  57. data/spec/assets/biff/no_cc_config.yml +27 -0
  58. data/spec/assets/biff/no_range_config.yml +27 -0
  59. data/spec/assets/biff/no_subnet_config.yml +16 -0
  60. data/spec/assets/biff/ok_network_config.yml +30 -0
  61. data/spec/assets/biff/properties_template.erb +6 -0
  62. data/spec/assets/deployment.MF +0 -0
  63. data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
  64. data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
  65. data/spec/assets/release/jobs/cacher.tgz +0 -0
  66. data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
  67. data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
  68. data/spec/assets/release/jobs/cacher/job.MF +6 -0
  69. data/spec/assets/release/jobs/cacher/monit +1 -0
  70. data/spec/assets/release/jobs/cleaner.tgz +0 -0
  71. data/spec/assets/release/jobs/cleaner/job.MF +4 -0
  72. data/spec/assets/release/jobs/cleaner/monit +1 -0
  73. data/spec/assets/release/jobs/sweeper.tgz +0 -0
  74. data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
  75. data/spec/assets/release/jobs/sweeper/job.MF +5 -0
  76. data/spec/assets/release/jobs/sweeper/monit +1 -0
  77. data/spec/assets/release/packages/mutator.tar.gz +0 -0
  78. data/spec/assets/release/packages/stuff.tgz +0 -0
  79. data/spec/assets/release/release.MF +17 -0
  80. data/spec/assets/release_invalid_checksum.tgz +0 -0
  81. data/spec/assets/release_invalid_jobs.tgz +0 -0
  82. data/spec/assets/release_no_name.tgz +0 -0
  83. data/spec/assets/release_no_version.tgz +0 -0
  84. data/spec/assets/stemcell/image +1 -0
  85. data/spec/assets/stemcell/stemcell.MF +6 -0
  86. data/spec/assets/stemcell_invalid_mf.tgz +0 -0
  87. data/spec/assets/stemcell_no_image.tgz +0 -0
  88. data/spec/assets/valid_release.tgz +0 -0
  89. data/spec/assets/valid_stemcell.tgz +0 -0
  90. data/spec/spec_helper.rb +25 -0
  91. data/spec/unit/base_command_spec.rb +66 -0
  92. data/spec/unit/biff_spec.rb +135 -0
  93. data/spec/unit/cache_spec.rb +36 -0
  94. data/spec/unit/cli_commands_spec.rb +481 -0
  95. data/spec/unit/config_spec.rb +139 -0
  96. data/spec/unit/core_ext_spec.rb +77 -0
  97. data/spec/unit/dependency_helper_spec.rb +52 -0
  98. data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
  99. data/spec/unit/director_spec.rb +511 -0
  100. data/spec/unit/director_task_spec.rb +48 -0
  101. data/spec/unit/event_log_renderer_spec.rb +171 -0
  102. data/spec/unit/hash_changeset_spec.rb +73 -0
  103. data/spec/unit/job_builder_spec.rb +454 -0
  104. data/spec/unit/package_builder_spec.rb +567 -0
  105. data/spec/unit/release_builder_spec.rb +65 -0
  106. data/spec/unit/release_spec.rb +66 -0
  107. data/spec/unit/release_tarball_spec.rb +33 -0
  108. data/spec/unit/runner_spec.rb +140 -0
  109. data/spec/unit/ssh_spec.rb +78 -0
  110. data/spec/unit/stemcell_spec.rb +17 -0
  111. data/spec/unit/version_calc_spec.rb +27 -0
  112. data/spec/unit/versions_index_spec.rb +132 -0
  113. metadata +338 -0
data/README ADDED
@@ -0,0 +1,4 @@
1
+ Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ BOSH CLI is a command-line interface to VMware AppCloud Outer Shell.
4
+
@@ -0,0 +1,55 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ $:.unshift(File.expand_path("../../rake", __FILE__))
4
+
5
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __FILE__)
6
+
7
+ require "rubygems"
8
+ require "bundler"
9
+
10
+ Bundler.setup(:default, :test)
11
+
12
+ require "rake"
13
+ begin
14
+ require "rspec/core/rake_task"
15
+ rescue LoadError
16
+ end
17
+
18
+ require "bundler_task"
19
+ require "ci_task"
20
+
21
+ if defined?(RSpec)
22
+ namespace :spec do
23
+ SPEC_OPTS = %w(--format progress --colour)
24
+
25
+ desc "Run unit tests"
26
+ unit_rspec_task = RSpec::Core::RakeTask.new(:unit) do |t|
27
+ t.pattern = "spec/unit/**/*_spec.rb"
28
+ t.rspec_opts = SPEC_OPTS
29
+ end
30
+
31
+ CiTask.new do |task|
32
+ task.rspec_task = unit_rspec_task
33
+ end
34
+ end
35
+
36
+ desc "Run tests"
37
+ task :spec => %w(spec:unit)
38
+
39
+ desc "Run tests for CIs"
40
+ task "spec:ci" => %w(spec:unit:ci)
41
+ end
42
+
43
+ BundlerTask.new
44
+
45
+ gem_helper = Bundler::GemHelper.new(Dir.pwd)
46
+
47
+ desc "Build BOSH CLI gem into the pkg directory"
48
+ task "build" do
49
+ gem_helper.build_gem
50
+ end
51
+
52
+ desc "Build and install BOSH CLI into system gems"
53
+ task "install_head" do
54
+ gem_helper.install_gem
55
+ end
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ gemfile = File.expand_path("../../Gemfile", __FILE__)
4
+
5
+ if File.exists?(gemfile)
6
+ ENV["BUNDLE_GEMFILE"] = gemfile
7
+ require "rubygems"
8
+ require "bundler/setup"
9
+ end
10
+
11
+ $:.unshift(File.expand_path("../../lib", __FILE__))
12
+ require "cli"
13
+
14
+ trap("INT") { puts "\nExiting..."; exit(1) }
15
+
16
+ Thread.abort_on_exception = true
17
+ Bosh::Cli::Runner.run(ARGV.dup)
@@ -0,0 +1,76 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh
4
+ module Cli
5
+ DEFAULT_CONFIG_PATH = File.expand_path("~/.bosh_config")
6
+ DEFAULT_CACHE_DIR = File.expand_path("~/.bosh_cache")
7
+ end
8
+ end
9
+
10
+ autoload :HTTPClient, "httpclient"
11
+ Bosh.autoload :Blobstore, "blobstore_client"
12
+
13
+ require "base64"
14
+ require "digest/sha1"
15
+ require "fileutils"
16
+ require "highline/import"
17
+ require "json"
18
+ require "monitor"
19
+ require "optparse"
20
+ require "ostruct"
21
+ require "pathname"
22
+ require "progressbar"
23
+ require "set"
24
+ require "tempfile"
25
+ require "terminal-table/import"
26
+ require "time"
27
+ require "timeout"
28
+ require "tmpdir"
29
+ require "uri"
30
+ require "yaml"
31
+ require "netaddr"
32
+ require "net/ssh"
33
+ require "net/scp"
34
+ require "net/ssh/gateway"
35
+
36
+ unless defined?(Bosh::Cli::VERSION)
37
+ require "cli/version"
38
+ end
39
+
40
+ require "cli/config"
41
+ require "cli/core_ext"
42
+ require "cli/errors"
43
+ require "cli/yaml_helper"
44
+ require "cli/version_calc"
45
+ require "cli/dependency_helper"
46
+ require "cli/deployment_helper"
47
+ require "cli/validation"
48
+ require "cli/cache"
49
+ require "cli/stemcell"
50
+ require "cli/director"
51
+ require "cli/director_task"
52
+
53
+ require "cli/versions_index"
54
+ require "cli/packaging_helper"
55
+ require "cli/package_builder"
56
+ require "cli/job_builder"
57
+ require "cli/changeset_helper"
58
+ require "cli/task_log_renderer"
59
+ require "cli/event_log_renderer"
60
+ require "cli/deployment_manifest_compiler"
61
+
62
+ require "cli/release"
63
+ require "cli/release_builder"
64
+ require "cli/release_compiler"
65
+ require "cli/release_tarball"
66
+
67
+ require "cli/command_definition"
68
+ require "cli/runner"
69
+
70
+ YAML::ENGINE.yamler = 'syck' if defined?(YAML::ENGINE.yamler)
71
+
72
+ require File.expand_path(File.dirname(__FILE__) + "/cli/commands/base")
73
+
74
+ Dir[File.dirname(__FILE__) + "/cli/commands/*.rb"].each do |file|
75
+ require File.expand_path(file)
76
+ end
@@ -0,0 +1,44 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ class Cache
5
+
6
+ attr_reader :cache_dir
7
+
8
+ def initialize(cache_dir = nil)
9
+ @cache_dir = cache_dir || Bosh::Cli::DEFAULT_CACHE_DIR
10
+
11
+ if File.exists?(@cache_dir) && !File.directory?(@cache_dir)
12
+ raise CacheDirectoryError, "BOSH cache directory error: " +
13
+ "`#{@cache_dir}' is a file, not directory"
14
+ end
15
+
16
+ unless File.exists?(@cache_dir)
17
+ FileUtils.mkdir_p(@cache_dir)
18
+ File.chmod(0700, @cache_dir)
19
+ end
20
+ end
21
+
22
+ def read(key)
23
+ cached_file = path(key)
24
+ return nil unless File.exists?(cached_file)
25
+ File.read(cached_file)
26
+ end
27
+
28
+ def write(key, value)
29
+ File.open(path(key), "w") do |f|
30
+ f.write(value)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def path(key)
37
+ File.expand_path(hash_for(key), @cache_dir)
38
+ end
39
+
40
+ def hash_for(key)
41
+ Digest::SHA1.hexdigest(key)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,142 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ class HashChangeset
5
+ class FormatError < StandardError; end
6
+
7
+ attr_accessor :values
8
+
9
+ def initialize
10
+ @children = {}
11
+ @values = { :old => nil, :new => nil }
12
+ end
13
+
14
+ def [](key)
15
+ @children[key.to_s]
16
+ end
17
+
18
+ def keys
19
+ @children.keys
20
+ end
21
+
22
+ def []=(key, value)
23
+ @children[key.to_s] = value
24
+ end
25
+
26
+ def add_hash(hash, as)
27
+ unless hash.is_a?(Hash)
28
+ raise FormatError, "Trying to add #{hash.class} to a changeset, " +
29
+ "Hash expected"
30
+ end
31
+
32
+ self.values[as] = hash
33
+
34
+ hash.each_pair do |k, v|
35
+ self[k] ||= HashChangeset.new
36
+ self[k].values[as] = v
37
+
38
+ if v.is_a?(Hash)
39
+ self[k].add_hash(v, as)
40
+ end
41
+ end
42
+ end
43
+
44
+ def leaf?
45
+ @children.empty?
46
+ end
47
+
48
+ def each(&block)
49
+ @children.each_value { |v| yield v }
50
+ end
51
+
52
+ def summary(level = 0)
53
+ indent = " " * level
54
+ out = []
55
+
56
+ @children.each_pair do |k, v|
57
+ if v.state == :mismatch
58
+ out << indent + "#{k} type changed: ".yellow +
59
+ "#{v.old.class.to_s} -> #{v.new.class.to_s}"
60
+ out << diff(v.old, v.new, indent + " ")
61
+ elsif v.leaf?
62
+ case v.state
63
+ when :added
64
+ out << indent + "added #{k}: ".yellow + v.new.to_s
65
+ when :removed
66
+ out << indent + "removed #{k}: ".red + v.old.to_s
67
+ when :changed
68
+ out << indent + "changed #{k}: ".yellow
69
+ out << diff(v.old, v.new, indent + " ")
70
+ end
71
+ else
72
+ # TODO: track renames?
73
+ child_summary = v.summary(level + 1)
74
+
75
+ unless child_summary.empty?
76
+ out << indent + k
77
+ out << child_summary
78
+ end
79
+ end
80
+ end
81
+ out
82
+ end
83
+
84
+ def diff(old_value, new_value, indent)
85
+ old_value = [old_value] unless old_value.kind_of?(Array)
86
+ new_value = [new_value] unless new_value.kind_of?(Array)
87
+
88
+ added = new_value - old_value
89
+ removed = old_value - new_value
90
+
91
+ lines = []
92
+
93
+ # Ruby 1.8 has ugly Hash#to_s, hence the normalization
94
+
95
+ removed.each do |line|
96
+ line = line.inspect if line.is_a?(Hash)
97
+ lines << "#{indent}- #{line}".red
98
+ end
99
+
100
+ added.each do |line|
101
+ line = line.inspect if line.is_a?(Hash)
102
+ lines << "#{indent}+ #{line}".green
103
+ end
104
+
105
+ lines.join("\n")
106
+ end
107
+
108
+ def old
109
+ @values[:old]
110
+ end
111
+
112
+ def new
113
+ @values[:new]
114
+ end
115
+
116
+ def state
117
+ if old.nil? && new.nil?
118
+ :none
119
+ elsif old.nil? && !new.nil?
120
+ :added
121
+ elsif !old.nil? && new.nil?
122
+ :removed
123
+ elsif old.class != new.class && !(boolean?(old) && boolean?(new))
124
+ :mismatch
125
+ elsif old == new
126
+ :same
127
+ else
128
+ :changed
129
+ end
130
+ end
131
+
132
+ [:added, :removed, :mismatch, :changed, :same].each do |s|
133
+ define_method("#{s}?".to_sym) do
134
+ state == s
135
+ end
136
+ end
137
+
138
+ def boolean?(value)
139
+ value.kind_of?(TrueClass) || value.kind_of?(FalseClass)
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,52 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ class CommandDefinition
5
+ attr_reader :options
6
+ attr_reader :power_options
7
+ attr_reader :keywords
8
+
9
+ def initialize
10
+ @options = []
11
+ @power_options = []
12
+ @hints = []
13
+ @keywords = []
14
+ end
15
+
16
+ def usage(str = nil)
17
+ if str
18
+ @usage = str.strip
19
+ @keywords = str.split(/\s+/).select do |word|
20
+ word.match(/^[a-z]/i)
21
+ end
22
+ else
23
+ @usage
24
+ end
25
+ end
26
+
27
+ def description(str = nil)
28
+ str ? (@description = str.to_s.strip) : @description
29
+ end
30
+
31
+ alias :desc :description
32
+
33
+ def option(name, value = "")
34
+ @options << [name, value]
35
+ end
36
+
37
+ def power_option(name, value = "")
38
+ @power_options << [name, value]
39
+ end
40
+
41
+ def route(*args, &block)
42
+ if args.size > 0
43
+ @route = args
44
+ elsif block_given?
45
+ @route = block
46
+ else
47
+ @route
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,245 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ module Command
5
+ class Base
6
+ BLOBS_DIR = "blobs"
7
+ BLOBS_INDEX_FILE = "blob_index.yml"
8
+
9
+ attr_reader :cache, :config, :options, :work_dir
10
+ attr_accessor :out, :usage
11
+
12
+ def initialize(options = {})
13
+ @options = options.dup
14
+ @work_dir = Dir.pwd
15
+ config_file = @options[:config] || Bosh::Cli::DEFAULT_CONFIG_PATH
16
+ cache_dir = @options[:cache_dir] || Bosh::Cli::DEFAULT_CACHE_DIR
17
+ @config = Config.new(config_file)
18
+ @cache = Cache.new(cache_dir)
19
+ end
20
+
21
+ class << self
22
+ attr_reader :commands
23
+
24
+ def command(name, &block)
25
+ @commands ||= {}
26
+ @commands[name] = block
27
+ end
28
+ end
29
+
30
+ def director
31
+ @director ||= Bosh::Cli::Director.new(target, username, password)
32
+ end
33
+
34
+ def release
35
+ return @release if @release
36
+ check_if_release_dir
37
+ @release = Bosh::Cli::Release.new(@work_dir)
38
+ end
39
+
40
+ def blobstore
41
+ release.blobstore
42
+ end
43
+
44
+ def logged_in?
45
+ username && password
46
+ end
47
+
48
+ def non_interactive?
49
+ !interactive?
50
+ end
51
+
52
+ def interactive?
53
+ !options[:non_interactive]
54
+ end
55
+
56
+ def verbose?
57
+ options[:verbose]
58
+ end
59
+
60
+ # TODO: implement it
61
+ def dry_run?
62
+ options[:dry_run]
63
+ end
64
+
65
+ def show_usage
66
+ say("Usage: #{@usage}") if @usage
67
+ end
68
+
69
+ def run(namespace, action, *args)
70
+ eval(namespace.to_s.capitalize).new(options).send(action.to_sym, *args)
71
+ end
72
+
73
+ def redirect(*args)
74
+ run(*args)
75
+ raise Bosh::Cli::GracefulExit, "redirected to %s" % [args.join(" ")]
76
+ end
77
+
78
+ def confirmed?(question = "Are you sure?")
79
+ non_interactive? ||
80
+ ask("#{question} (type 'yes' to continue): ") == "yes"
81
+ end
82
+
83
+ [:username, :password, :target, :deployment].each do |attr_name|
84
+ define_method attr_name do
85
+ config.send(attr_name)
86
+ end
87
+ end
88
+
89
+ alias_method :target_url, :target
90
+
91
+ def target_name
92
+ config.target_name || target_url
93
+ end
94
+
95
+ def target_version
96
+ config.target_version ? "Ver: " + config.target_version : ""
97
+ end
98
+
99
+ def full_target_name
100
+ # TODO refactor this method
101
+ ret = (target_name.blank? || target_name == target_url ?
102
+ target_name : "%s (%s)" % [target_name, target_url])
103
+ ret + " %s" % target_version if ret
104
+ end
105
+
106
+ ##
107
+ # Returns whether there is currently a task running. A wrapper for the
108
+ # director.rb method.
109
+ #
110
+ # @return [Boolean] Whether there is a task currently running.
111
+ def task_running?
112
+ director.has_current?
113
+ end
114
+
115
+ ##
116
+ # Cancels the task currently running. A wrapper for the director.rb
117
+ # method.
118
+ def cancel_current_task
119
+ director.cancel_current
120
+ end
121
+
122
+ protected
123
+
124
+ def auth_required
125
+ target_required
126
+ err("Please log in first") unless logged_in?
127
+ end
128
+
129
+ def target_required
130
+ err("Please choose target first") if target.nil?
131
+ end
132
+
133
+ def deployment_required
134
+ err("Please choose deployment first") if deployment.nil?
135
+ end
136
+
137
+ def check_if_release_dir
138
+ unless in_release_dir?
139
+ err("Sorry, your current directory doesn't look " +
140
+ "like release directory")
141
+ end
142
+ end
143
+
144
+ def check_if_dirty_state
145
+ if dirty_state?
146
+ say("\n%s\n" % [`git status`])
147
+ err("Your current directory has some local modifications, " +
148
+ "please discard or commit them first")
149
+ end
150
+ end
151
+
152
+ def in_release_dir?
153
+ File.directory?("packages") &&
154
+ File.directory?("jobs") &&
155
+ File.directory?("src")
156
+ end
157
+
158
+ def dirty_state?
159
+ `which git`
160
+ return false unless $? == 0
161
+ File.directory?(".git") && `git status --porcelain | wc -l`.to_i > 0
162
+ end
163
+
164
+ def normalize_url(url)
165
+ url = "http://#{url}" unless url.match(/^https?/)
166
+ URI.parse(url).to_s
167
+ end
168
+
169
+ def check_if_blobs_supported
170
+ check_if_release_dir
171
+ unless File.directory?(BLOBS_DIR)
172
+ err("Can't find blob directory '#{BLOBS_DIR}'.")
173
+ end
174
+
175
+ unless File.file?(BLOBS_INDEX_FILE)
176
+ err("Can't find '#{BLOBS_INDEX_FILE}'")
177
+ end
178
+ end
179
+
180
+ def check_dirty_blobs
181
+ if File.file?(File.join(work_dir, BLOBS_INDEX_FILE)) && blob_status != 0
182
+ err("Your 'blobs' directory is not in sync. " +
183
+ "Resolve using 'bosh sync blobs' command")
184
+ end
185
+ end
186
+
187
+ def get_blobs_index
188
+ load_yaml_file(File.join(work_dir, BLOBS_INDEX_FILE))
189
+ end
190
+
191
+ def blob_status(verbose = false)
192
+ check_if_blobs_supported
193
+ untracked = []
194
+ modified = []
195
+ tracked= []
196
+ unsynced = []
197
+
198
+ local_blobs = {}
199
+ Dir.chdir(BLOBS_DIR) do
200
+ Dir.glob("**/*").select { |entry| File.file?(entry) }.each do |file|
201
+ local_blobs[file] = Digest::SHA1.file(file).hexdigest
202
+ end
203
+ end
204
+ remote_blobs = get_blobs_index
205
+
206
+ local_blobs.each do |blob_name, blob_sha|
207
+ if remote_blobs[blob_name].nil?
208
+ untracked << blob_name
209
+ elsif blob_sha != remote_blobs[blob_name]["sha"]
210
+ modified << blob_name
211
+ else
212
+ tracked << blob_name
213
+ end
214
+ end
215
+
216
+ remote_blobs.each_key do |blob_name|
217
+ unsynced << blob_name if local_blobs[blob_name].nil?
218
+ end
219
+
220
+ changes = modified.size + untracked.size + unsynced.size
221
+ return changes unless verbose
222
+
223
+ if modified.size > 0
224
+ say("\nModified blobs ('bosh upload blob' to update): ".green)
225
+ modified.each { |blob| say(blob) }
226
+ end
227
+
228
+ if untracked.size > 0
229
+ say("\nNew blobs ('bosh upload blob' to add): ".green)
230
+ untracked.each { |blob| say(blob) }
231
+ end
232
+
233
+ if unsynced.size > 0
234
+ say("\nMissing blobs ('bosh sync blobs' to fetch) : ".green)
235
+ unsynced.each { |blob| say(blob) }
236
+ end
237
+
238
+ if changes == 0
239
+ say("\nRelease blobs are up to date".green)
240
+ end
241
+ changes
242
+ end
243
+ end
244
+ end
245
+ end