hammer_cli 0.0.4

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 (38) hide show
  1. data/LICENSE +5 -0
  2. data/README.md +105 -0
  3. data/bin/hammer +53 -0
  4. data/config/cli_config.template.yml +14 -0
  5. data/doc/design.png +0 -0
  6. data/doc/design.uml +24 -0
  7. data/hammer_cli_complete +13 -0
  8. data/lib/hammer_cli/abstract.rb +95 -0
  9. data/lib/hammer_cli/apipie/command.rb +75 -0
  10. data/lib/hammer_cli/apipie/options.rb +97 -0
  11. data/lib/hammer_cli/apipie/read_command.rb +58 -0
  12. data/lib/hammer_cli/apipie/resource.rb +54 -0
  13. data/lib/hammer_cli/apipie/write_command.rb +35 -0
  14. data/lib/hammer_cli/apipie.rb +3 -0
  15. data/lib/hammer_cli/autocompletion.rb +46 -0
  16. data/lib/hammer_cli/exception_handler.rb +69 -0
  17. data/lib/hammer_cli/exit_codes.rb +23 -0
  18. data/lib/hammer_cli/logger.rb +50 -0
  19. data/lib/hammer_cli/logger_watch.rb +14 -0
  20. data/lib/hammer_cli/main.rb +53 -0
  21. data/lib/hammer_cli/messages.rb +55 -0
  22. data/lib/hammer_cli/option_formatters.rb +13 -0
  23. data/lib/hammer_cli/output/adapter/abstract.rb +27 -0
  24. data/lib/hammer_cli/output/adapter/base.rb +143 -0
  25. data/lib/hammer_cli/output/adapter/silent.rb +17 -0
  26. data/lib/hammer_cli/output/adapter/table.rb +53 -0
  27. data/lib/hammer_cli/output/adapter.rb +6 -0
  28. data/lib/hammer_cli/output/definition.rb +17 -0
  29. data/lib/hammer_cli/output/dsl.rb +56 -0
  30. data/lib/hammer_cli/output/fields.rb +128 -0
  31. data/lib/hammer_cli/output/output.rb +27 -0
  32. data/lib/hammer_cli/output.rb +6 -0
  33. data/lib/hammer_cli/settings.rb +48 -0
  34. data/lib/hammer_cli/shell.rb +49 -0
  35. data/lib/hammer_cli/validator.rb +114 -0
  36. data/lib/hammer_cli/version.rb +5 -0
  37. data/lib/hammer_cli.rb +13 -0
  38. metadata +189 -0
@@ -0,0 +1,69 @@
1
+ require 'rest_client'
2
+ require 'logging'
3
+
4
+ module HammerCLI
5
+ class ExceptionHandler
6
+
7
+ def initialize options={}
8
+ @output = options[:output] or raise "Missing option output"
9
+ @logger = Logging.logger['Exception']
10
+ end
11
+
12
+ def mappings
13
+ [
14
+ [Exception, :handle_general_exception], # catch all
15
+ [RestClient::ResourceNotFound, :handle_not_found],
16
+ [RestClient::Unauthorized, :handle_unauthorized],
17
+ ]
18
+ end
19
+
20
+ def handle_exception e, options={}
21
+ @options = options
22
+ handler = mappings.reverse.find { |m| e.class.respond_to?(:"<=") ? e.class <= m[0] : false }
23
+ return send(handler[1], e) if handler
24
+ raise e
25
+ end
26
+
27
+ protected
28
+
29
+ def print_error error
30
+ error = error.join("\n") if error.kind_of? Array
31
+ @logger.error error
32
+
33
+ if @options[:heading]
34
+ @output.print_error @options[:heading], error
35
+ else
36
+ @output.print_error error
37
+ end
38
+ end
39
+
40
+ def log_full_error e
41
+ backtrace = e.backtrace || []
42
+ @logger.error "\n\n#{e.class} (#{e.message}):\n " +
43
+ backtrace.join("\n ")
44
+ "\n\n"
45
+ end
46
+
47
+ def handle_general_exception e
48
+ print_error "Error: " + e.message
49
+ log_full_error e
50
+ HammerCLI::EX_SOFTWARE
51
+ end
52
+
53
+ def handle_not_found e
54
+ print_error e.message
55
+ log_full_error e
56
+ HammerCLI::EX_NOT_FOUND
57
+ end
58
+
59
+ def handle_unauthorized e
60
+ print_error "Invalid username or password"
61
+ log_full_error e
62
+ HammerCLI::EX_UNAUTHORIZED
63
+ end
64
+
65
+ end
66
+ end
67
+
68
+
69
+
@@ -0,0 +1,23 @@
1
+ module HammerCLI
2
+ # taken from sysexits.h
3
+ EX_OK = 0 # successful termination
4
+ EX_USAGE = 64 # command line usage error
5
+ EX_DATAERR = 65 # data format error
6
+ EX_NOINPUT = 66 # cannot open input
7
+ EX_NOUSER = 67 # addressee unknown
8
+ EX_NOHOST = 68 # host name unknown
9
+ EX_UNAVAILABLE = 69 # service unavailable
10
+ EX_SOFTWARE = 70 # internal software error
11
+ EX_OSERR = 71 # system error (e.g., can't fork)
12
+ EX_OSFILE = 72 # critical OS file missing
13
+ EX_CANTCREAT = 73 # can't create (user) output file
14
+ EX_IOERR = 74 # input/output error
15
+ EX_TEMPFAIL = 75 # temp failure; user is invited to retry
16
+ EX_PROTOCOL = 76 # remote error in protocol
17
+ EX_NOPERM = 77 # permission denied
18
+ EX_CONFIG = 78 # configuration error
19
+
20
+ # non POSIX codes
21
+ EX_NOT_FOUND = 128 # resource was not found
22
+ EX_UNAUTHORIZED = 129 # authorization failed
23
+ end
@@ -0,0 +1,50 @@
1
+ require 'fileutils'
2
+ require 'logging'
3
+
4
+ module HammerCLI
5
+ module Logger
6
+
7
+ Logging.color_scheme('bright',
8
+ :levels => {
9
+ :info => :green,
10
+ :warn => :yellow,
11
+ :error => :red,
12
+ :fatal => [:white, :on_red]},
13
+ :date => :blue,
14
+ :logger => :cyan,
15
+ :line => :yellow,
16
+ :file => :yellow,
17
+ :method => :yellow)
18
+
19
+ pattern = "[%5l %d %c] %m\n"
20
+ COLOR_LAYOUT = Logging::Layouts::Pattern.new(:pattern => pattern, :color_scheme => 'bright')
21
+ NOCOLOR_LAYOUT = Logging::Layouts::Pattern.new(:pattern => pattern, :color_scheme => nil)
22
+ DEFAULT_LOG_DIR = '/var/log/foreman'
23
+
24
+ log_dir = File.expand_path(HammerCLI::Settings[:log_dir] || DEFAULT_LOG_DIR)
25
+ begin
26
+ FileUtils.mkdir_p(log_dir, :mode => 0750)
27
+ rescue Errno::EACCES => e
28
+ puts "No permissions to create log dir #{log_dir}"
29
+ end
30
+
31
+ logger = Logging.logger.root
32
+ filename = "#{log_dir}/hammer.log"
33
+ begin
34
+ logger.appenders = ::Logging.appenders.rolling_file('configure',
35
+ :filename => filename,
36
+ :layout => NOCOLOR_LAYOUT,
37
+ :truncate => false,
38
+ :keep => 5,
39
+ :size => HammerCLI::Settings[:log_size] || 1024*1024) # 1MB
40
+ # set owner and group (it's ignored if attribute is nil)
41
+ FileUtils.chown HammerCLI::Settings[:log_owner], HammerCLI::Settings[:log_group], filename
42
+ rescue ArgumentError => e
43
+ puts "File #{filename} not writeable, won't log anything to file!"
44
+ end
45
+
46
+ logger.level = HammerCLI::Settings[:log_level]
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,14 @@
1
+ require 'awesome_print'
2
+
3
+ module HammerCLI
4
+ module Logger
5
+ module Watch
6
+ def watch(label, obj, options={})
7
+ if debug?
8
+ options = { :plain => HammerCLI::Settings[:watch_plain], :indent => -2 }.merge(options)
9
+ debug label + "\n" + obj.ai(options)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ require 'highline/import'
2
+
3
+ module HammerCLI
4
+
5
+ class MainCommand < AbstractCommand
6
+
7
+ option ["-v", "--verbose"], :flag, "be verbose"
8
+ option ["-c", "--config"], "CFG_FILE", "path to custom config file"
9
+
10
+ option ["-u", "--username"], "USERNAME", "username to access the remote system"
11
+ option ["-p", "--password"], "PASSWORD", "password to access the remote system"
12
+
13
+ option "--version", :flag, "show version" do
14
+ puts "hammer-%s" % HammerCLI.version
15
+ exit(HammerCLI::EX_OK)
16
+ end
17
+
18
+ option ["-P", "--ask-pass"], :flag, "Ask for password" do
19
+ context[:password] = get_password()
20
+ ''
21
+ end
22
+
23
+ option "--autocomplete", "LINE", "Get list of possible endings" do |line|
24
+ line = line.split
25
+ line.shift
26
+ endings = self.class.autocomplete(line).map { |l| l[0] }
27
+ puts endings.join(' ')
28
+ exit(HammerCLI::EX_OK)
29
+ end
30
+
31
+ def password=(password)
32
+ context[:password] = password.nil? ? ENV['FOREMAN_PASSWORD'] : password
33
+ end
34
+
35
+ def username=(username)
36
+ context[:username] = username.nil? ? ENV['FOREMAN_USERNAME'] : username
37
+ end
38
+
39
+ private
40
+
41
+ def get_password(prompt="Enter Password ")
42
+ ask(prompt) {|q| q.echo = false}
43
+ end
44
+
45
+
46
+
47
+ end
48
+
49
+ end
50
+
51
+ # extend MainCommand
52
+ require 'hammer_cli/shell'
53
+
@@ -0,0 +1,55 @@
1
+
2
+ module HammerCLI
3
+ module Messages
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def success_message_for action
10
+ self.class.success_message_for action
11
+ end
12
+
13
+ def success_message
14
+ self.class.success_message
15
+ end
16
+
17
+ def failure_message_for action
18
+ self.class.failure_message_for action
19
+ end
20
+
21
+ def failure_message
22
+ self.class.failure_message
23
+ end
24
+
25
+ def handle_exception e
26
+ exception_handler.handle_exception e, :heading => failure_message
27
+ end
28
+
29
+ module ClassMethods
30
+ def success_message_for action, msg=nil
31
+ @success_message ||= {}
32
+ @success_message[action] = msg unless msg.nil?
33
+ @success_message[action]
34
+ end
35
+
36
+ def success_message msg=nil
37
+ success_message_for :default, msg
38
+ end
39
+
40
+ def failure_message_for action, msg=nil
41
+ @failure_message ||= {}
42
+ @failure_message[action] = msg unless msg.nil?
43
+ @failure_message[action]
44
+ end
45
+
46
+ def failure_message msg=nil
47
+ failure_message_for :default, msg
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+
54
+
55
+
@@ -0,0 +1,13 @@
1
+ module HammerCLI
2
+ module OptionFormatters
3
+
4
+ def self.list(val)
5
+ val.is_a?(String) ? val.split(",") : []
6
+ end
7
+
8
+ def self.file(path)
9
+ File.read(File.expand_path(path))
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+
2
+ module HammerCLI::Output::Adapter
3
+
4
+ class Abstract
5
+
6
+ def print_message msg
7
+ puts msg
8
+ end
9
+
10
+ def print_error msg, details=nil
11
+ details = details.split("\n") if details.kind_of? String
12
+
13
+ if details
14
+ indent = " "
15
+ $stderr.puts msg+":"
16
+ $stderr.puts indent + details.join("\n"+indent)
17
+ else
18
+ $stderr.puts msg
19
+ end
20
+ end
21
+
22
+ def print_records fields, data, heading=nil
23
+ raise NotImplementedError
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,143 @@
1
+ module HammerCLI::Output::Adapter
2
+ class Base < Abstract
3
+
4
+ HEADING_LINE_WIDTH = 80
5
+ GROUP_INDENT = " "*2
6
+ LABEL_DIVIDER = ": "
7
+
8
+ def print_records fields, data, heading=nil
9
+ self.fields = fields
10
+
11
+ puts "-"*HEADING_LINE_WIDTH
12
+ puts " " + heading.to_s
13
+ puts "-"*HEADING_LINE_WIDTH
14
+
15
+ data.each do |d|
16
+ fields.collect do |f|
17
+ render_field(f, d)
18
+ end
19
+ puts
20
+ end
21
+ end
22
+
23
+ protected
24
+
25
+ def render_Field(field, data, indent="")
26
+ puts indent.to_s+" "+format_value(field, data).to_s
27
+ end
28
+
29
+ def render_Label(field, data, indent="")
30
+ render_label(field, indent)
31
+ puts
32
+ indent = indent.to_s + GROUP_INDENT
33
+
34
+ field.output_definition.fields.collect do |f|
35
+ render_field(f, data, indent)
36
+ end
37
+ end
38
+
39
+ def render_LabeledField(field, data, indent="")
40
+ render_label(field, indent)
41
+ puts format_value(field, data).to_s
42
+ end
43
+
44
+ def render_KeyValue(field, data, indent="")
45
+ render_label(field, indent)
46
+ params = field.get_value(data) || []
47
+ print "%{name} => %{value}" % params
48
+ end
49
+
50
+ def render_Collection(field, data, indent="")
51
+ render_label(field, indent)
52
+ puts
53
+ indent = indent.to_s + " "*(label_width_for_list(self.fields)+LABEL_DIVIDER.size)
54
+
55
+ field.get_value(data).each do |d|
56
+ field.output_definition.fields.collect do |f|
57
+ render_field(f, d, indent)
58
+ end
59
+ puts
60
+ end
61
+ end
62
+
63
+ def render_Joint(field, data, indent="")
64
+ render_label(field, indent)
65
+ data = field.get_value(data)
66
+ puts field.attributes.collect{|attr| data[attr] }.join(" ")
67
+ end
68
+
69
+ def format_Date(string_date)
70
+ t = DateTime.parse(string_date.to_s)
71
+ t.strftime("%Y/%m/%d %H:%M:%S")
72
+ rescue ArgumentError
73
+ ""
74
+ end
75
+
76
+ def format_List(list)
77
+ list.join(", ") if list
78
+ end
79
+
80
+ def format_OSName(os)
81
+ "%{name} %{major}.%{minor}" % os
82
+ end
83
+
84
+ def format_Server(server)
85
+ "%{name} (%{url})" % server
86
+ end
87
+
88
+
89
+ def render_field(field, data, indent="")
90
+ renderer = find_renderer(field)
91
+ renderer.call(field, data, indent)
92
+ end
93
+
94
+ def render_label(field, indent="")
95
+ if field.label
96
+ w = label_width_for_list(self.fields) - indent.size + 1
97
+ print indent.to_s+" %#{-w}s" % (field.label.to_s+LABEL_DIVIDER)
98
+ else
99
+ print indent.to_s+" "
100
+ end
101
+ end
102
+
103
+ def format_value(field, data)
104
+ format_method = "format_"+field_type(field.class)
105
+
106
+ value = field.get_value(data)
107
+ value = send(format_method, value) if respond_to?(format_method, true)
108
+ value
109
+ end
110
+
111
+ def find_renderer(field)
112
+ field.class.ancestors.each do |cls|
113
+ render_method = "render_"+field_type(cls)
114
+ return method(render_method) if respond_to?(render_method, true)
115
+ end
116
+ raise "No renderer found for field %s" % field.class
117
+ end
118
+
119
+ def field_type(field_class)
120
+ field_class.name.split("::")[-1]
121
+ end
122
+
123
+ def label_width_for(field)
124
+ if field.respond_to?(:output_definition)
125
+ label_width_for_list(field.output_definition.fields)+GROUP_INDENT.size
126
+ elsif field.respond_to?(:label)
127
+ field.label.size+LABEL_DIVIDER.size rescue 0
128
+ else
129
+ 0
130
+ end
131
+ end
132
+
133
+ def label_width_for_list(fields)
134
+ fields.inject(0) do |result, f|
135
+ width = label_width_for(f)
136
+ (width > result) ? width : result
137
+ end
138
+ end
139
+
140
+ attr_accessor :fields
141
+
142
+ end
143
+ end
@@ -0,0 +1,17 @@
1
+
2
+ module HammerCLI::Output::Adapter
3
+
4
+ class Silent
5
+
6
+ def print_message msg
7
+ end
8
+
9
+ def print_error msg, details=[]
10
+ end
11
+
12
+ def print_records fields, data, heading=nil
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,53 @@
1
+ require 'table_print'
2
+
3
+ module HammerCLI::Output::Adapter
4
+
5
+ class Table < Base
6
+
7
+ def print_records fields, data, heading=nil
8
+
9
+ rows = data.collect do |d|
10
+ row = {}
11
+ fields.each do |f|
12
+ row[f.label.to_sym] = f.get_value(d)
13
+ end
14
+ row
15
+ end
16
+
17
+ options = fields.collect do |f|
18
+ { f.label.to_sym => {
19
+ :formatters => [ Formatter.new(self, "format_"+field_type(f.class)) ] } }
20
+ end
21
+
22
+ printer = TablePrint::Printer.new(rows, options)
23
+ output = printer.table_print
24
+ dashes = /\n(-+)\n/.match output
25
+ puts dashes[1]
26
+ puts output
27
+ puts dashes[1]
28
+ end
29
+
30
+ def print_heading heading, size
31
+ size = heading.size if heading.size > size
32
+ puts '-' * size
33
+ puts ' ' * ((size-heading.size)/2) + heading
34
+ puts '-' * size
35
+ end
36
+
37
+ end
38
+
39
+ class Formatter
40
+ def initialize adapter, method
41
+ @adapter = adapter
42
+ @method = method
43
+ end
44
+
45
+ def format value
46
+ if @adapter.respond_to?(@method, true)
47
+ value = @adapter.send(@method, value)
48
+ end
49
+ value
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,6 @@
1
+ require File.join(File.dirname(__FILE__), 'adapter/abstract')
2
+ require File.join(File.dirname(__FILE__), 'adapter/base')
3
+ require File.join(File.dirname(__FILE__), 'adapter/table')
4
+ require File.join(File.dirname(__FILE__), 'adapter/silent')
5
+
6
+
@@ -0,0 +1,17 @@
1
+ module HammerCLI::Output
2
+
3
+ class Definition
4
+
5
+ attr_accessor :fields
6
+
7
+ def initialize
8
+ @fields = []
9
+ end
10
+
11
+ def append(fields)
12
+ @fields += fields
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,56 @@
1
+ module HammerCLI::Output
2
+
3
+ class Dsl
4
+
5
+ def initialize options={}
6
+ @current_path = options[:path] || []
7
+ end
8
+
9
+ def fields
10
+ @fields ||= []
11
+ @fields
12
+ end
13
+
14
+ def build &block
15
+ self.instance_eval &block
16
+ end
17
+
18
+ protected
19
+
20
+ def field key, label, type=nil, options={}, &block
21
+ options[:path] = current_path.clone << key
22
+ options[:label] = label
23
+ type ||= HammerCLI::Output::DataField
24
+ custom_field type, options, &block
25
+ end
26
+
27
+ def custom_field type, options={}, &block
28
+ self.fields << type.new(options, &block)
29
+ end
30
+
31
+ def label label, &block
32
+ options = {}
33
+ options[:path] = current_path.clone
34
+ options[:label] = label
35
+ custom_field HammerCLI::Output::Fields::Label, options, &block
36
+ end
37
+
38
+ def from key
39
+ self.current_path.push key
40
+ yield if block_given?
41
+ self.current_path.pop
42
+ end
43
+
44
+ def collection key, label, options={}, &block
45
+ field key, label, HammerCLI::Output::Fields::Collection, options, &block
46
+ end
47
+
48
+
49
+ def current_path
50
+ @current_path ||= []
51
+ @current_path
52
+ end
53
+
54
+ end
55
+
56
+ end