bosh_cli 0.19.6 → 1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/bin/bosh +3 -0
  2. data/lib/cli.rb +15 -5
  3. data/lib/cli/{commands/base.rb → base_command.rb} +38 -44
  4. data/lib/cli/command_discovery.rb +40 -0
  5. data/lib/cli/command_handler.rb +135 -0
  6. data/lib/cli/commands/biff.rb +16 -12
  7. data/lib/cli/commands/blob_management.rb +10 -3
  8. data/lib/cli/commands/cloudcheck.rb +13 -11
  9. data/lib/cli/commands/complete.rb +29 -0
  10. data/lib/cli/commands/deployment.rb +137 -28
  11. data/lib/cli/commands/help.rb +96 -0
  12. data/lib/cli/commands/job.rb +4 -1
  13. data/lib/cli/commands/job_management.rb +36 -23
  14. data/lib/cli/commands/job_rename.rb +11 -12
  15. data/lib/cli/commands/log_management.rb +28 -32
  16. data/lib/cli/commands/maintenance.rb +6 -1
  17. data/lib/cli/commands/misc.rb +129 -87
  18. data/lib/cli/commands/package.rb +6 -65
  19. data/lib/cli/commands/property_management.rb +20 -8
  20. data/lib/cli/commands/release.rb +211 -206
  21. data/lib/cli/commands/ssh.rb +178 -188
  22. data/lib/cli/commands/stemcell.rb +114 -51
  23. data/lib/cli/commands/task.rb +74 -56
  24. data/lib/cli/commands/user.rb +6 -3
  25. data/lib/cli/commands/vms.rb +17 -15
  26. data/lib/cli/config.rb +27 -1
  27. data/lib/cli/core_ext.rb +27 -1
  28. data/lib/cli/deployment_helper.rb +47 -0
  29. data/lib/cli/director.rb +18 -9
  30. data/lib/cli/errors.rb +6 -0
  31. data/lib/cli/job_builder.rb +75 -23
  32. data/lib/cli/job_property_collection.rb +87 -0
  33. data/lib/cli/job_property_validator.rb +130 -0
  34. data/lib/cli/package_builder.rb +32 -5
  35. data/lib/cli/release.rb +2 -0
  36. data/lib/cli/release_builder.rb +9 -13
  37. data/lib/cli/release_compiler.rb +5 -34
  38. data/lib/cli/release_tarball.rb +4 -19
  39. data/lib/cli/runner.rb +118 -694
  40. data/lib/cli/version.rb +1 -1
  41. data/spec/assets/config/swift-hp/config/final.yml +6 -0
  42. data/spec/assets/config/swift-hp/config/private.yml +7 -0
  43. data/spec/assets/config/swift-rackspace/config/final.yml +6 -0
  44. data/spec/assets/config/swift-rackspace/config/private.yml +6 -0
  45. data/spec/spec_helper.rb +0 -5
  46. data/spec/unit/base_command_spec.rb +32 -37
  47. data/spec/unit/biff_spec.rb +11 -10
  48. data/spec/unit/cli_commands_spec.rb +96 -88
  49. data/spec/unit/core_ext_spec.rb +1 -1
  50. data/spec/unit/deployment_manifest_spec.rb +36 -0
  51. data/spec/unit/director_spec.rb +17 -3
  52. data/spec/unit/job_builder_spec.rb +2 -2
  53. data/spec/unit/job_property_collection_spec.rb +111 -0
  54. data/spec/unit/job_property_validator_spec.rb +7 -0
  55. data/spec/unit/job_rename_spec.rb +7 -6
  56. data/spec/unit/package_builder_spec.rb +2 -2
  57. data/spec/unit/release_builder_spec.rb +33 -0
  58. data/spec/unit/release_spec.rb +54 -0
  59. data/spec/unit/release_tarball_spec.rb +2 -7
  60. data/spec/unit/runner_spec.rb +1 -151
  61. data/spec/unit/ssh_spec.rb +15 -9
  62. metadata +41 -12
  63. data/lib/cli/command_definition.rb +0 -52
  64. data/lib/cli/templates/help_message.erb +0 -80
data/bin/bosh CHANGED
@@ -14,6 +14,9 @@ require "cli"
14
14
  begin
15
15
  Thread.abort_on_exception = true
16
16
  Bosh::Cli::Runner.run(ARGV.dup)
17
+ rescue Errno::EPIPE
18
+ puts("pipe closed, exiting...")
19
+ exit(0)
17
20
  rescue Interrupt
18
21
  puts "\nExiting..."
19
22
  exit(1)
data/lib/cli.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  module Bosh
4
4
  module Cli
5
5
  DEFAULT_CONFIG_PATH = File.expand_path("~/.bosh_config")
6
- DEFAULT_CACHE_DIR = File.expand_path("~/.bosh_cache")
6
+ DEFAULT_CACHE_DIR = File.expand_path("~/.bosh_cache")
7
7
  end
8
8
  end
9
9
 
@@ -70,13 +70,23 @@ require "cli/release_tarball"
70
70
 
71
71
  require "cli/blob_manager"
72
72
 
73
- require "cli/command_definition"
73
+ require "common/properties"
74
+ require "cli/job_property_collection"
75
+ require "cli/job_property_validator"
76
+
77
+ require "cli/command_discovery"
78
+ require "cli/command_handler"
74
79
  require "cli/runner"
80
+ require "cli/base_command"
75
81
 
76
- YAML::ENGINE.yamler = 'syck' if defined?(YAML::ENGINE.yamler)
82
+ if defined?(YAML::ENGINE.yamler)
83
+ YAML::ENGINE.yamler = "syck"
84
+ end
77
85
 
78
- require File.expand_path(File.dirname(__FILE__) + "/cli/commands/base")
86
+ tmpdir = Dir.mktmpdir
87
+ at_exit { FileUtils.rm_rf(tmpdir) }
88
+ ENV["TMPDIR"] = tmpdir
79
89
 
80
90
  Dir[File.dirname(__FILE__) + "/cli/commands/*.rb"].each do |file|
81
- require File.expand_path(file)
91
+ require file
82
92
  end
@@ -3,31 +3,50 @@
3
3
  module Bosh::Cli
4
4
  module Command
5
5
  class Base
6
- attr_reader :cache, :config, :options, :work_dir
7
- attr_accessor :out, :usage
6
+ extend Bosh::Cli::CommandDiscovery
7
+
8
+ attr_reader :options
9
+ attr_reader :work_dir
10
+ attr_reader :runner
11
+
12
+ attr_accessor :out
13
+
14
+ # @return [Array] Arguments passed to command handler
15
+ attr_accessor :args
8
16
 
9
17
  DEFAULT_DIRECTOR_PORT = 25555
10
18
 
11
- def initialize(options = {})
12
- @options = options.dup
19
+ # @param [Bosh::Cli::Runner] runner
20
+ def initialize(runner = nil)
21
+ @runner = runner
22
+ @options = {}
13
23
  @work_dir = Dir.pwd
14
- config_file = @options[:config] || Bosh::Cli::DEFAULT_CONFIG_PATH
15
- @config = Config.new(config_file)
16
- @cache = Config.cache
17
24
  @exit_code = 0
18
25
  @out = nil
19
- @usage = nil
26
+ @args = []
20
27
  end
21
28
 
22
- class << self
23
- attr_reader :commands
29
+ # @return [Bosh::Cli::Cache] Current CLI cache
30
+ def cache
31
+ Config.cache
32
+ end
24
33
 
25
- def command(name, &block)
26
- @commands ||= {}
27
- @commands[name] = block
34
+ # @return [Bosh::Cli::Config] Current configuration
35
+ def config
36
+ @config ||= begin
37
+ config_file = options[:config] || Bosh::Cli::DEFAULT_CONFIG_PATH
38
+ Bosh::Cli::Config.new(config_file)
28
39
  end
29
40
  end
30
41
 
42
+ def add_option(name, value)
43
+ @options[name] = value
44
+ end
45
+
46
+ def remove_option(name)
47
+ @options.delete(name)
48
+ end
49
+
31
50
  def director
32
51
  @director ||= Bosh::Cli::Director.new(target, username, password)
33
52
  end
@@ -51,38 +70,24 @@ module Bosh::Cli
51
70
  end
52
71
 
53
72
  def non_interactive?
54
- !interactive?
73
+ options[:non_interactive]
55
74
  end
56
75
 
57
76
  def interactive?
58
- !options[:non_interactive]
77
+ !non_interactive?
59
78
  end
60
79
 
61
80
  def verbose?
62
- options[:verbose]
63
- end
64
-
65
- # TODO: implement it
66
- def dry_run?
67
- options[:dry_run]
68
- end
69
-
70
- def show_usage
71
- say("Usage: #{@usage}") if @usage
72
- end
73
-
74
- def run(namespace, action, *args)
75
- eval(namespace.to_s.capitalize).new(options).send(action.to_sym, *args)
81
+ @options[:verbose]
76
82
  end
77
83
 
78
84
  def redirect(*args)
79
- run(*args)
80
- raise Bosh::Cli::GracefulExit, "redirected to %s" % [args.join(" ")]
85
+ Bosh::Cli::Runner.new(args, @options).run
81
86
  end
82
87
 
83
88
  def confirmed?(question = "Are you sure?")
84
- non_interactive? ||
85
- ask("#{question} (type 'yes' to continue): ") == "yes"
89
+ return true if non_interactive?
90
+ ask("#{question} (type 'yes' to continue): ") == "yes"
86
91
  end
87
92
 
88
93
  # @return [String] Target director URL
@@ -112,17 +117,6 @@ module Bosh::Cli
112
117
  config.target_name || target_url
113
118
  end
114
119
 
115
- def target_version
116
- config.target_version ? "Ver: " + config.target_version : ""
117
- end
118
-
119
- def full_target_name
120
- # TODO refactor this method
121
- ret = (target_name.blank? || target_name == target_url ?
122
- target_name : "%s (%s)" % [target_name, target_url])
123
- ret + " %s" % target_version if ret
124
- end
125
-
126
120
  # Sets or returns command exit code
127
121
  # @param [optional,Integer] code If param is given, sets exit code. If
128
122
  # it's nil, returns previously set exit_code
@@ -0,0 +1,40 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ module CommandDiscovery
5
+
6
+ def usage(string = nil)
7
+ @usage = string
8
+ end
9
+
10
+ def desc(string)
11
+ @desc = string
12
+ end
13
+
14
+ def option(name, *args)
15
+ (@options ||= []) << [name, args]
16
+ end
17
+
18
+ # @param [Symbol] method_name Method name
19
+ def method_added(method_name)
20
+ if @usage && @desc
21
+ @options ||= []
22
+ method = instance_method(method_name)
23
+ register_command(method, @usage, @desc, @options)
24
+ end
25
+ @usage = nil
26
+ @desc = nil
27
+ @options = []
28
+ end
29
+
30
+ # @param [UnboundMethod] method Method implementing the command
31
+ # @param [String] usage Command usage (used to parse command)
32
+ # @param [String] desc Command description
33
+ # @param [Array] options Command options
34
+ def register_command(method, usage, desc, options = [])
35
+ command = CommandHandler.new(self, method, usage, desc, options)
36
+ Bosh::Cli::Config.register_command(command)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,135 @@
1
+ # Copyright (c) 2009-2012 VMware, Inc.
2
+
3
+ module Bosh::Cli
4
+ class CommandHandler
5
+
6
+ # @return [Array]
7
+ attr_reader :keywords
8
+
9
+ # @return [String]
10
+ attr_reader :usage
11
+
12
+ # @return [String]
13
+ attr_reader :desc
14
+
15
+ # @return [Bosh::Cli::Runner]
16
+ attr_accessor :runner
17
+
18
+ # @param [Class] klass
19
+ # @param [UnboundMethod] method
20
+ # @param [String] usage
21
+ # @param [String] desc
22
+ def initialize(klass, method, usage, desc, options = [])
23
+ @klass = klass
24
+ @method = method
25
+ @usage = usage
26
+ @desc = desc
27
+
28
+ @options = options
29
+
30
+ @hints = []
31
+ @keywords = []
32
+
33
+ @parser = OptionParser.new
34
+ @runner = nil
35
+ extract_keywords
36
+ end
37
+
38
+ # Run handler with provided args
39
+ # @param [Array] args
40
+ # @return [Integer] Command exit code
41
+ def run(args, extra_options = {})
42
+ handler = @klass.new(@runner)
43
+
44
+ @options.each do |(name, arguments)|
45
+ @parser.on(name, *arguments) do |value|
46
+ handler.add_option(format_option_name(name), value)
47
+ end
48
+ end
49
+
50
+ extra_options.each_pair do |name, value|
51
+ handler.add_option(format_option_name(name), value)
52
+ end
53
+
54
+ args = parse_options(args)
55
+
56
+ begin
57
+ handler.send(@method.name, *args)
58
+ handler.exit_code
59
+ rescue ArgumentError => e
60
+ say(e.message)
61
+ err("Usage: #{usage_with_params}")
62
+ end
63
+ end
64
+
65
+ def usage_with_params
66
+ result = [@usage]
67
+ @method.parameters.each do |parameter|
68
+ next if parameter.size < 2
69
+ kind, name = parameter
70
+ if kind == :opt
71
+ result << "[<#{name}>]"
72
+ elsif kind == :req
73
+ result << "<#{name}>"
74
+ end
75
+ end
76
+
77
+ @options.each do |(name, _)|
78
+ result << "[#{name}]"
79
+ end
80
+
81
+ result.join(" ")
82
+ end
83
+
84
+ def has_options?
85
+ @options.size > 0
86
+ end
87
+
88
+ def options_summary
89
+ result = []
90
+ padding = 5
91
+
92
+ margin = @options.inject(0) do |max, (name, _)|
93
+ [max, name.size].max
94
+ end
95
+
96
+ @options.each do |(name, desc)|
97
+ desc = desc.select { |word| word.is_a?(String) }
98
+ column_width = terminal_width - padding - margin
99
+
100
+ result << name.ljust(margin).yellow + " " +
101
+ desc.join(" ").columnize(
102
+ column_width, [margin + 1, name.size + 1].max)
103
+ end
104
+
105
+ result.join("\n")
106
+ end
107
+
108
+ # @param [Array] args Arguments to parse
109
+ def parse_options(args)
110
+ @parser.parse!(args)
111
+ end
112
+
113
+ private
114
+
115
+ def format_option_name(name)
116
+ case name
117
+ when Symbol
118
+ name
119
+ when String
120
+ name.split(/\s+/)[0].gsub(/^-*/, "").gsub("-", "_").to_sym
121
+ else
122
+ name
123
+ end
124
+ end
125
+
126
+ def extract_keywords
127
+ words = @usage.split(/\s+/)
128
+ words.each do |word|
129
+ break unless word.match(/^[a-z]/i)
130
+ @keywords << word
131
+ end
132
+ end
133
+
134
+ end
135
+ end
@@ -8,22 +8,26 @@ module Bosh::Cli::Command
8
8
  # are diff'd and the user can choose to keep the new config.
9
9
  # @param [String] template The string path to the template that should be
10
10
  # used.
11
+ #
12
+ usage "diff"
13
+ desc "Diffs your current BOSH deployment configuration against " +
14
+ "the specified BOSH deployment configuration template so that " +
15
+ "you can keep your deployment configuration file up to date. " +
16
+ "A dev template can be found in deployments repos."
11
17
  def biff(template)
12
- begin
13
- setup(template)
18
+ setup(template)
14
19
 
15
- template_to_fill = ERB.new(File.read(@template_file), 0, "%<>-")
16
- @template_output = template_to_fill.result(binding)
20
+ template_to_fill = ERB.new(File.read(@template_file), 0, "%<>-")
21
+ @template_output = template_to_fill.result(binding)
17
22
 
18
- if @errors == 0
19
- print_string_diff(File.read(@deployment_file), @template_output)
20
- keep_new_file unless @no_differences
21
- else
22
- say("There were " + "#{@errors} errors.".red)
23
- end
24
- ensure
25
- delete_temp_diff_files
23
+ if @errors == 0
24
+ print_string_diff(File.read(@deployment_file), @template_output)
25
+ keep_new_file unless @no_differences
26
+ else
27
+ err("There were #{@errors} errors.")
26
28
  end
29
+ ensure
30
+ delete_temp_diff_files
27
31
  end
28
32
 
29
33
  private
@@ -4,6 +4,8 @@ module Bosh::Cli::Command
4
4
  class BlobManagement < Base
5
5
 
6
6
  # Prints out blobs status
7
+ usage "blobs"
8
+ desc "Print current blobs status"
7
9
  def status
8
10
  blob_manager.print_status
9
11
  end
@@ -11,11 +13,13 @@ module Bosh::Cli::Command
11
13
  # Adds blob to managed blobs
12
14
  # @param [String] local_path Local file path
13
15
  # @param [optional, String] blob_dir Directory to store blob in, relative
14
- # to blobs dir
16
+ # to blobs dir
17
+ usage "add blob"
18
+ desc "Add a local file as BOSH blob"
15
19
  def add(local_path, blob_dir = nil)
16
20
  blob_path = File.basename(local_path)
17
21
  if blob_dir
18
- # We don't need about blobs prefix,
22
+ # We don't need 'blobs/' prefix,
19
23
  # but it might be handy for people who rely on auto-completion
20
24
  if blob_dir[0..5] == "blobs/"
21
25
  blob_dir = blob_dir[6..-1]
@@ -26,6 +30,8 @@ module Bosh::Cli::Command
26
30
  end
27
31
 
28
32
  # Uploads all blobs that need to be uploaded
33
+ usage "upload blobs"
34
+ desc "Upload new and updated blobs to the blobstore"
29
35
  def upload
30
36
  blob_manager.print_status
31
37
 
@@ -38,10 +44,11 @@ module Bosh::Cli::Command
38
44
  end
39
45
 
40
46
  # Syncs blobs with blobstore
47
+ usage "sync blobs"
48
+ desc "Sync blob with the blobstore"
41
49
  def sync
42
50
  blob_manager.sync
43
51
  blob_manager.print_status
44
52
  end
45
-
46
53
  end
47
54
  end
@@ -4,23 +4,25 @@ module Bosh::Cli::Command
4
4
  class CloudCheck < Base
5
5
  include Bosh::Cli::DeploymentHelper
6
6
 
7
- def perform(*options)
7
+ # bosh cloudcheck
8
+ usage "cloudcheck"
9
+ desc "Cloud consistency check and interactive repair"
10
+ option "--auto",
11
+ "resolve problems automatically ",
12
+ "(not recommended for production)"
13
+ option "--report",
14
+ "generate report only, ",
15
+ "don't attempt to resolve problems"
16
+ def perform(deployment_name = nil)
8
17
  auth_required
9
- # TODO: introduce option helpers
10
- deployment_name = options.shift unless options[0] =~ /^--/
11
-
12
- @auto_mode = options.delete("--auto")
13
- @report_mode = options.delete("--report")
18
+ @auto_mode = options[:auto]
19
+ @report_mode = options[:report]
14
20
 
15
21
  if non_interactive? && !@report_mode
16
22
  err ("Cloudcheck cannot be run in non-interactive mode\n" +
17
23
  "Please use `--auto' flag if you want automated resolutions")
18
24
  end
19
25
 
20
- if options.size > 0
21
- err("Unknown options: #{options.join(", ")}")
22
- end
23
-
24
26
  if @auto_mode && @report_mode
25
27
  err("Can't use --auto and --report mode together")
26
28
  end
@@ -90,7 +92,7 @@ module Bosh::Cli::Command
90
92
 
91
93
  if @problems.empty?
92
94
  say("No problems found".green)
93
- quit
95
+ exit(0)
94
96
  end
95
97
 
96
98
  @problems.each do |problem|