bosh_cli 0.16
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -0
- data/Rakefile +55 -0
- data/bin/bosh +17 -0
- data/lib/cli.rb +76 -0
- data/lib/cli/cache.rb +44 -0
- data/lib/cli/changeset_helper.rb +142 -0
- data/lib/cli/command_definition.rb +52 -0
- data/lib/cli/commands/base.rb +245 -0
- data/lib/cli/commands/biff.rb +300 -0
- data/lib/cli/commands/blob.rb +125 -0
- data/lib/cli/commands/cloudcheck.rb +169 -0
- data/lib/cli/commands/deployment.rb +147 -0
- data/lib/cli/commands/job.rb +42 -0
- data/lib/cli/commands/job_management.rb +117 -0
- data/lib/cli/commands/log_management.rb +81 -0
- data/lib/cli/commands/maintenance.rb +131 -0
- data/lib/cli/commands/misc.rb +240 -0
- data/lib/cli/commands/package.rb +112 -0
- data/lib/cli/commands/property_management.rb +125 -0
- data/lib/cli/commands/release.rb +469 -0
- data/lib/cli/commands/ssh.rb +271 -0
- data/lib/cli/commands/stemcell.rb +184 -0
- data/lib/cli/commands/task.rb +213 -0
- data/lib/cli/commands/user.rb +28 -0
- data/lib/cli/commands/vms.rb +53 -0
- data/lib/cli/config.rb +154 -0
- data/lib/cli/core_ext.rb +145 -0
- data/lib/cli/dependency_helper.rb +62 -0
- data/lib/cli/deployment_helper.rb +263 -0
- data/lib/cli/deployment_manifest_compiler.rb +28 -0
- data/lib/cli/director.rb +633 -0
- data/lib/cli/director_task.rb +64 -0
- data/lib/cli/errors.rb +48 -0
- data/lib/cli/event_log_renderer.rb +351 -0
- data/lib/cli/job_builder.rb +226 -0
- data/lib/cli/package_builder.rb +254 -0
- data/lib/cli/packaging_helper.rb +248 -0
- data/lib/cli/release.rb +176 -0
- data/lib/cli/release_builder.rb +215 -0
- data/lib/cli/release_compiler.rb +178 -0
- data/lib/cli/release_tarball.rb +272 -0
- data/lib/cli/runner.rb +771 -0
- data/lib/cli/stemcell.rb +83 -0
- data/lib/cli/task_log_renderer.rb +40 -0
- data/lib/cli/templates/help_message.erb +75 -0
- data/lib/cli/validation.rb +42 -0
- data/lib/cli/version.rb +7 -0
- data/lib/cli/version_calc.rb +48 -0
- data/lib/cli/versions_index.rb +126 -0
- data/lib/cli/yaml_helper.rb +62 -0
- data/spec/assets/biff/bad_gateway_config.yml +28 -0
- data/spec/assets/biff/good_simple_config.yml +63 -0
- data/spec/assets/biff/good_simple_golden_config.yml +63 -0
- data/spec/assets/biff/good_simple_template.erb +69 -0
- data/spec/assets/biff/multiple_subnets_config.yml +40 -0
- data/spec/assets/biff/network_only_template.erb +34 -0
- data/spec/assets/biff/no_cc_config.yml +27 -0
- data/spec/assets/biff/no_range_config.yml +27 -0
- data/spec/assets/biff/no_subnet_config.yml +16 -0
- data/spec/assets/biff/ok_network_config.yml +30 -0
- data/spec/assets/biff/properties_template.erb +6 -0
- data/spec/assets/deployment.MF +0 -0
- data/spec/assets/plugins/bosh/cli/commands/echo.rb +43 -0
- data/spec/assets/plugins/bosh/cli/commands/ruby.rb +24 -0
- data/spec/assets/release/jobs/cacher.tgz +0 -0
- data/spec/assets/release/jobs/cacher/config/file1.conf +0 -0
- data/spec/assets/release/jobs/cacher/config/file2.conf +0 -0
- data/spec/assets/release/jobs/cacher/job.MF +6 -0
- data/spec/assets/release/jobs/cacher/monit +1 -0
- data/spec/assets/release/jobs/cleaner.tgz +0 -0
- data/spec/assets/release/jobs/cleaner/job.MF +4 -0
- data/spec/assets/release/jobs/cleaner/monit +1 -0
- data/spec/assets/release/jobs/sweeper.tgz +0 -0
- data/spec/assets/release/jobs/sweeper/config/test.conf +1 -0
- data/spec/assets/release/jobs/sweeper/job.MF +5 -0
- data/spec/assets/release/jobs/sweeper/monit +1 -0
- data/spec/assets/release/packages/mutator.tar.gz +0 -0
- data/spec/assets/release/packages/stuff.tgz +0 -0
- data/spec/assets/release/release.MF +17 -0
- data/spec/assets/release_invalid_checksum.tgz +0 -0
- data/spec/assets/release_invalid_jobs.tgz +0 -0
- data/spec/assets/release_no_name.tgz +0 -0
- data/spec/assets/release_no_version.tgz +0 -0
- data/spec/assets/stemcell/image +1 -0
- data/spec/assets/stemcell/stemcell.MF +6 -0
- data/spec/assets/stemcell_invalid_mf.tgz +0 -0
- data/spec/assets/stemcell_no_image.tgz +0 -0
- data/spec/assets/valid_release.tgz +0 -0
- data/spec/assets/valid_stemcell.tgz +0 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/unit/base_command_spec.rb +66 -0
- data/spec/unit/biff_spec.rb +135 -0
- data/spec/unit/cache_spec.rb +36 -0
- data/spec/unit/cli_commands_spec.rb +481 -0
- data/spec/unit/config_spec.rb +139 -0
- data/spec/unit/core_ext_spec.rb +77 -0
- data/spec/unit/dependency_helper_spec.rb +52 -0
- data/spec/unit/deployment_manifest_compiler_spec.rb +63 -0
- data/spec/unit/director_spec.rb +511 -0
- data/spec/unit/director_task_spec.rb +48 -0
- data/spec/unit/event_log_renderer_spec.rb +171 -0
- data/spec/unit/hash_changeset_spec.rb +73 -0
- data/spec/unit/job_builder_spec.rb +454 -0
- data/spec/unit/package_builder_spec.rb +567 -0
- data/spec/unit/release_builder_spec.rb +65 -0
- data/spec/unit/release_spec.rb +66 -0
- data/spec/unit/release_tarball_spec.rb +33 -0
- data/spec/unit/runner_spec.rb +140 -0
- data/spec/unit/ssh_spec.rb +78 -0
- data/spec/unit/stemcell_spec.rb +17 -0
- data/spec/unit/version_calc_spec.rb +27 -0
- data/spec/unit/versions_index_spec.rb +132 -0
- metadata +338 -0
data/README
ADDED
data/Rakefile
ADDED
@@ -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
|
data/bin/bosh
ADDED
@@ -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)
|
data/lib/cli.rb
ADDED
@@ -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
|
data/lib/cli/cache.rb
ADDED
@@ -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
|