hammer_cli 0.0.18 → 0.1.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.
- checksums.yaml +4 -4
- data/README.md +3 -314
- data/bin/hammer +45 -6
- data/config/cli.modules.d/module_config_template.yml +4 -0
- data/config/cli_config.template.yml +9 -11
- data/doc/developer_docs.md +1 -0
- data/doc/i18n.md +85 -0
- data/doc/installation.md +321 -0
- data/lib/hammer_cli.rb +3 -0
- data/lib/hammer_cli/abstract.rb +15 -24
- data/lib/hammer_cli/apipie/command.rb +13 -7
- data/lib/hammer_cli/apipie/options.rb +14 -16
- data/lib/hammer_cli/apipie/read_command.rb +6 -1
- data/lib/hammer_cli/apipie/resource.rb +48 -58
- data/lib/hammer_cli/apipie/write_command.rb +5 -1
- data/lib/hammer_cli/completer.rb +77 -21
- data/lib/hammer_cli/connection.rb +44 -0
- data/lib/hammer_cli/exception_handler.rb +15 -4
- data/lib/hammer_cli/exceptions.rb +6 -0
- data/lib/hammer_cli/i18n.rb +95 -0
- data/lib/hammer_cli/logger.rb +3 -3
- data/lib/hammer_cli/main.rb +12 -11
- data/lib/hammer_cli/modules.rb +19 -6
- data/lib/hammer_cli/options/normalizers.rb +42 -7
- data/lib/hammer_cli/options/option_definition.rb +2 -2
- data/lib/hammer_cli/output.rb +1 -0
- data/lib/hammer_cli/output/adapter/abstract.rb +20 -0
- data/lib/hammer_cli/output/adapter/base.rb +49 -78
- data/lib/hammer_cli/output/adapter/csv.rb +5 -5
- data/lib/hammer_cli/output/adapter/table.rb +41 -10
- data/lib/hammer_cli/output/dsl.rb +1 -1
- data/lib/hammer_cli/output/field_filter.rb +21 -0
- data/lib/hammer_cli/output/fields.rb +44 -78
- data/lib/hammer_cli/output/formatters.rb +38 -0
- data/lib/hammer_cli/settings.rb +28 -6
- data/lib/hammer_cli/shell.rb +58 -57
- data/lib/hammer_cli/utils.rb +14 -0
- data/lib/hammer_cli/validator.rb +5 -5
- data/lib/hammer_cli/version.rb +1 -1
- data/locale/Makefile +64 -0
- data/locale/hammer-cli.pot +203 -0
- data/locale/zanata.xml +29 -0
- data/test/unit/apipie/command_test.rb +42 -25
- data/test/unit/apipie/read_command_test.rb +10 -7
- data/test/unit/apipie/write_command_test.rb +9 -8
- data/test/unit/completer_test.rb +206 -21
- data/test/unit/connection_test.rb +68 -0
- data/test/unit/fixtures/apipie/architectures.json +153 -0
- data/test/unit/fixtures/apipie/documented.json +79 -0
- data/test/unit/fixtures/json_input/invalid.json +12 -0
- data/test/unit/fixtures/json_input/valid.json +12 -0
- data/test/unit/history_test.rb +71 -0
- data/test/unit/main_test.rb +9 -0
- data/test/unit/modules_test.rb +22 -6
- data/test/unit/options/field_filter_test.rb +27 -0
- data/test/unit/options/normalizers_test.rb +53 -0
- data/test/unit/output/adapter/base_test.rb +162 -10
- data/test/unit/output/adapter/csv_test.rb +16 -3
- data/test/unit/output/adapter/table_test.rb +97 -13
- data/test/unit/output/dsl_test.rb +74 -6
- data/test/unit/output/fields_test.rb +93 -62
- data/test/unit/output/formatters_test.rb +47 -0
- data/test/unit/settings_test.rb +35 -4
- data/test/unit/utils_test.rb +45 -0
- metadata +85 -4
- data/test/unit/apipie/fake_api.rb +0 -101
@@ -0,0 +1,44 @@
|
|
1
|
+
module HammerCLI
|
2
|
+
|
3
|
+
class AbstractConnector
|
4
|
+
def initialize(params={})
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Connection
|
9
|
+
|
10
|
+
def self.drop(name)
|
11
|
+
connections.delete(name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.drop_all()
|
15
|
+
connections.keys.each { |c| drop(c) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.create(name, conector_params={}, options={})
|
19
|
+
unless connections[name]
|
20
|
+
Logging.logger['Connection'].debug "Registered: #{name}"
|
21
|
+
connector = options[:connector] || AbstractConnector
|
22
|
+
|
23
|
+
connections[name] = connector.new(conector_params)
|
24
|
+
end
|
25
|
+
connections[name]
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.exist?(name)
|
29
|
+
!get(name).nil?
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.get(name)
|
33
|
+
connections[name]
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def self.connections
|
39
|
+
@connections_hash ||= {}
|
40
|
+
@connections_hash
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -16,6 +16,7 @@ module HammerCLI
|
|
16
16
|
[Clamp::UsageError, :handle_usage_exception],
|
17
17
|
[RestClient::ResourceNotFound, :handle_not_found],
|
18
18
|
[RestClient::Unauthorized, :handle_unauthorized],
|
19
|
+
[ApipieBindings::DocLoadingError, :handle_apipie_docloading_error],
|
19
20
|
]
|
20
21
|
end
|
21
22
|
|
@@ -49,19 +50,20 @@ module HammerCLI
|
|
49
50
|
|
50
51
|
def log_full_error(e)
|
51
52
|
backtrace = e.backtrace || []
|
52
|
-
|
53
|
+
error = "\n\n#{e.class} (#{e.message}):\n " +
|
53
54
|
backtrace.join("\n ")
|
54
55
|
"\n\n"
|
56
|
+
@logger.error error
|
55
57
|
end
|
56
58
|
|
57
59
|
def handle_general_exception(e)
|
58
|
-
print_error "Error: "
|
60
|
+
print_error _("Error: %s") % e.message
|
59
61
|
log_full_error e
|
60
62
|
HammerCLI::EX_SOFTWARE
|
61
63
|
end
|
62
64
|
|
63
65
|
def handle_usage_exception(e)
|
64
|
-
print_error "Error: %
|
66
|
+
print_error _("Error: %{message}\n\nSee: '%{path} --help'") % {:message => e.message, :path => e.command.invocation_path}
|
65
67
|
log_full_error e
|
66
68
|
HammerCLI::EX_USAGE
|
67
69
|
end
|
@@ -78,11 +80,20 @@ module HammerCLI
|
|
78
80
|
end
|
79
81
|
|
80
82
|
def handle_unauthorized(e)
|
81
|
-
print_error "Invalid username or password"
|
83
|
+
print_error _("Invalid username or password")
|
82
84
|
log_full_error e
|
83
85
|
HammerCLI::EX_UNAUTHORIZED
|
84
86
|
end
|
85
87
|
|
88
|
+
def handle_apipie_docloading_error(e)
|
89
|
+
rake_command = "rake apipie:cache"
|
90
|
+
print_error _("Could not load API description from the server\n"\
|
91
|
+
" - is your server down?\n"\
|
92
|
+
" - was \"#{rake_command}\" run on the server when using apipie cache? (typical production settings))\n")
|
93
|
+
log_full_error e
|
94
|
+
HammerCLI::EX_CONFIG
|
95
|
+
end
|
96
|
+
|
86
97
|
end
|
87
98
|
end
|
88
99
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'fast_gettext'
|
2
|
+
require 'locale'
|
3
|
+
|
4
|
+
module HammerCLI
|
5
|
+
module I18n
|
6
|
+
|
7
|
+
module AllDomains
|
8
|
+
def _(key)
|
9
|
+
FastGettext::TranslationMultidomain.D_(key)
|
10
|
+
end
|
11
|
+
|
12
|
+
def n_(*keys)
|
13
|
+
FastGettext::TranslationMultidomain.Dn_(*keys)
|
14
|
+
end
|
15
|
+
|
16
|
+
def s_(key, separator=nil)
|
17
|
+
FastGettext::TranslationMultidomain.Ds_(key, separator)
|
18
|
+
end
|
19
|
+
|
20
|
+
def ns_(*keys)
|
21
|
+
FastGettext::TranslationMultidomain.Dns_(*keys)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
class AbstractLocaleDomain
|
27
|
+
|
28
|
+
def available_locales
|
29
|
+
Dir.glob(locale_dir+'/*').select { |f| File.directory? f }.map { |f| File.basename(f) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def translated_files
|
33
|
+
[]
|
34
|
+
end
|
35
|
+
|
36
|
+
def type
|
37
|
+
:mo
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :locale_dir, :domain_name
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
class LocaleDomain < AbstractLocaleDomain
|
45
|
+
|
46
|
+
def translated_files
|
47
|
+
Dir.glob(File.join(File.dirname(__FILE__), '../**/*.rb'))
|
48
|
+
end
|
49
|
+
|
50
|
+
def locale_dir
|
51
|
+
File.join(File.dirname(__FILE__), '../../locale')
|
52
|
+
end
|
53
|
+
|
54
|
+
def domain_name
|
55
|
+
'hammer-cli'
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def self.locale
|
62
|
+
lang_variant = Locale.current.to_simple.to_str
|
63
|
+
lang = lang_variant.gsub(/_.*/, "")
|
64
|
+
|
65
|
+
hammer_domain = HammerCLI::I18n::LocaleDomain.new
|
66
|
+
if hammer_domain.available_locales.include? lang_variant
|
67
|
+
lang_variant
|
68
|
+
else
|
69
|
+
lang
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
def self.domains
|
75
|
+
@domains ||= []
|
76
|
+
@domains
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.add_domain(domain)
|
80
|
+
domains << domain
|
81
|
+
FastGettext.add_text_domain(domain.domain_name, :path => domain.locale_dir, :type => domain.type, :report_warning => false)
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
Encoding.default_external='UTF-8' if defined? Encoding
|
86
|
+
FastGettext.locale = locale
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
include FastGettext::Translation
|
92
|
+
include HammerCLI::I18n::AllDomains
|
93
|
+
|
94
|
+
HammerCLI::I18n.add_domain(HammerCLI::I18n::LocaleDomain.new)
|
95
|
+
|
data/lib/hammer_cli/logger.rb
CHANGED
@@ -19,13 +19,13 @@ module HammerCLI
|
|
19
19
|
pattern = "[%5l %d %c] %m\n"
|
20
20
|
COLOR_LAYOUT = Logging::Layouts::Pattern.new(:pattern => pattern, :color_scheme => 'bright')
|
21
21
|
NOCOLOR_LAYOUT = Logging::Layouts::Pattern.new(:pattern => pattern, :color_scheme => nil)
|
22
|
-
DEFAULT_LOG_DIR = '/var/log/
|
22
|
+
DEFAULT_LOG_DIR = '/var/log/hammer'
|
23
23
|
|
24
24
|
log_dir = File.expand_path(HammerCLI::Settings.get(:log_dir) || DEFAULT_LOG_DIR)
|
25
25
|
begin
|
26
26
|
FileUtils.mkdir_p(log_dir, :mode => 0750)
|
27
27
|
rescue Errno::EACCES => e
|
28
|
-
puts "No permissions to create log dir
|
28
|
+
puts _("No permissions to create log dir %s") % log_dir
|
29
29
|
end
|
30
30
|
|
31
31
|
logger = Logging.logger.root
|
@@ -40,7 +40,7 @@ module HammerCLI
|
|
40
40
|
# set owner and group (it's ignored if attribute is nil)
|
41
41
|
FileUtils.chown HammerCLI::Settings.get(:log_owner), HammerCLI::Settings.get(:log_group), filename
|
42
42
|
rescue ArgumentError => e
|
43
|
-
puts "File
|
43
|
+
puts _("File %s not writeable, won't log anything to the file!") % filename
|
44
44
|
end
|
45
45
|
|
46
46
|
logger.level = HammerCLI::Settings.get(:log_level)
|
data/lib/hammer_cli/main.rb
CHANGED
@@ -4,15 +4,16 @@ module HammerCLI
|
|
4
4
|
|
5
5
|
class MainCommand < AbstractCommand
|
6
6
|
|
7
|
-
option ["-v", "--verbose"], :flag, "be verbose"
|
8
|
-
option ["-
|
7
|
+
option ["-v", "--verbose"], :flag, _("be verbose"), :context_target => :verbose
|
8
|
+
option ["-d", "--debug"], :flag, _("show debugging output "), :context_target => :debug
|
9
|
+
option ["-c", "--config"], "CFG_FILE", _("path to custom config file")
|
9
10
|
|
10
|
-
option ["-u", "--username"], "USERNAME", "username to access the remote system",
|
11
|
+
option ["-u", "--username"], "USERNAME", _("username to access the remote system"),
|
11
12
|
:context_target => :username
|
12
|
-
option ["-p", "--password"], "PASSWORD", "password to access the remote system",
|
13
|
+
option ["-p", "--password"], "PASSWORD", _("password to access the remote system"),
|
13
14
|
:context_target => :password
|
14
15
|
|
15
|
-
option "--version", :flag, "show version" do
|
16
|
+
option "--version", :flag, _("show version") do
|
16
17
|
puts "hammer (%s)" % HammerCLI.version
|
17
18
|
HammerCLI::Modules.names.each do |m|
|
18
19
|
module_version = HammerCLI::Modules.find_by_name(m).version
|
@@ -21,21 +22,21 @@ module HammerCLI
|
|
21
22
|
exit(HammerCLI::EX_OK)
|
22
23
|
end
|
23
24
|
|
24
|
-
option ["--show-ids"], :flag, "Show ids of associated resources",
|
25
|
+
option ["--show-ids"], :flag, _("Show ids of associated resources"),
|
25
26
|
:context_target => :show_ids
|
26
|
-
option ["--interactive"], "INTERACTIVE", "Explicitly turn interactive mode on/off",
|
27
|
+
option ["--interactive"], "INTERACTIVE", _("Explicitly turn interactive mode on/off"),
|
27
28
|
:format => HammerCLI::Options::Normalizers::Bool.new,
|
28
29
|
:context_target => :interactive
|
29
30
|
|
30
|
-
option ["--csv"], :flag, "Output as CSV (same as --output=csv)"
|
31
|
-
option ["--output"], "ADAPTER", "Set output format. One of [%s]" %
|
31
|
+
option ["--csv"], :flag, _("Output as CSV (same as --output=csv)")
|
32
|
+
option ["--output"], "ADAPTER", _("Set output format. One of [%s]") %
|
32
33
|
HammerCLI::Output::Output.adapters.keys.join(', '),
|
33
34
|
:context_target => :adapter
|
34
|
-
option ["--csv-separator"], "SEPARATOR", "Character to separate the values",
|
35
|
+
option ["--csv-separator"], "SEPARATOR", _("Character to separate the values"),
|
35
36
|
:context_target => :csv_separator
|
36
37
|
|
37
38
|
|
38
|
-
option "--autocomplete", "LINE", "Get list of possible endings" do |line|
|
39
|
+
option "--autocomplete", "LINE", _("Get list of possible endings") do |line|
|
39
40
|
# get rid of word 'hammer' on the line
|
40
41
|
line = line.to_s.gsub(/^\S+/, '')
|
41
42
|
|
data/lib/hammer_cli/modules.rb
CHANGED
@@ -4,7 +4,22 @@ module HammerCLI
|
|
4
4
|
class Modules
|
5
5
|
|
6
6
|
def self.names
|
7
|
-
|
7
|
+
|
8
|
+
# legacy modules config
|
9
|
+
modules = HammerCLI::Settings.get(:modules) || []
|
10
|
+
logger.warn _("Legacy configuration of modules detected. Check section about configuration in user manual") unless modules.empty?
|
11
|
+
|
12
|
+
HammerCLI::Settings.dump.inject(modules) do |names, (mod_name, mod_config)|
|
13
|
+
if mod_config.kind_of?(Hash) && !mod_config[:enable_module].nil?
|
14
|
+
mod = ["hammer_cli_#{mod_name}"]
|
15
|
+
if mod_config[:enable_module]
|
16
|
+
names += mod
|
17
|
+
else
|
18
|
+
names -= mod # disable when enabled in legacy config
|
19
|
+
end
|
20
|
+
end
|
21
|
+
names
|
22
|
+
end
|
8
23
|
end
|
9
24
|
|
10
25
|
def self.find_by_name(name)
|
@@ -22,13 +37,11 @@ module HammerCLI
|
|
22
37
|
def self.load!(name)
|
23
38
|
begin
|
24
39
|
require_module(name)
|
25
|
-
rescue LoadError => e
|
26
|
-
logger.error "Module #{name} not found"
|
27
|
-
raise e
|
28
40
|
rescue Exception => e
|
29
41
|
logger.error "Error while loading module #{name}"
|
30
|
-
|
31
|
-
|
42
|
+
puts _("Warning: An error occured while loading module %s") % name
|
43
|
+
# with ModuleLoadingError we assume the error is already logged by the issuer
|
44
|
+
logger.error e unless e.is_a?(HammerCLI::ModuleLoadingError)
|
32
45
|
raise e
|
33
46
|
end
|
34
47
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
1
3
|
module HammerCLI
|
2
4
|
module Options
|
3
5
|
module Normalizers
|
@@ -21,7 +23,7 @@ module HammerCLI
|
|
21
23
|
class KeyValueList < AbstractNormalizer
|
22
24
|
|
23
25
|
def description
|
24
|
-
"Comma-separated list of key=value."
|
26
|
+
_("Comma-separated list of key=value.")
|
25
27
|
end
|
26
28
|
|
27
29
|
def format(val)
|
@@ -30,7 +32,7 @@ module HammerCLI
|
|
30
32
|
val.split(",").inject({}) do |result, item|
|
31
33
|
parts = item.split("=")
|
32
34
|
if parts.size != 2
|
33
|
-
raise ArgumentError, "value must be defined as a comma-separated list of key=value"
|
35
|
+
raise ArgumentError, _("value must be defined as a comma-separated list of key=value")
|
34
36
|
end
|
35
37
|
result.update(parts[0] => parts[1])
|
36
38
|
end
|
@@ -41,7 +43,7 @@ module HammerCLI
|
|
41
43
|
class List < AbstractNormalizer
|
42
44
|
|
43
45
|
def description
|
44
|
-
"Comma separated list of values."
|
46
|
+
_("Comma separated list of values.")
|
45
47
|
end
|
46
48
|
|
47
49
|
def format(val)
|
@@ -53,7 +55,7 @@ module HammerCLI
|
|
53
55
|
class Bool < AbstractNormalizer
|
54
56
|
|
55
57
|
def description
|
56
|
-
"One of true/false, yes/no, 1/0."
|
58
|
+
_("One of true/false, yes/no, 1/0.")
|
57
59
|
end
|
58
60
|
|
59
61
|
def format(bool)
|
@@ -63,7 +65,7 @@ module HammerCLI
|
|
63
65
|
elsif bool.downcase.match(/^(false|f|no|n|0)$/i)
|
64
66
|
return false
|
65
67
|
else
|
66
|
-
raise ArgumentError, "value must be one of true/false, yes/no, 1/0"
|
68
|
+
raise ArgumentError, _("value must be one of true/false, yes/no, 1/0")
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
@@ -90,6 +92,23 @@ module HammerCLI
|
|
90
92
|
end
|
91
93
|
end
|
92
94
|
|
95
|
+
class JSONInput < File
|
96
|
+
|
97
|
+
def format(val)
|
98
|
+
# The JSON input can be either the path to a file whose contents are
|
99
|
+
# JSON or a JSON string. For example:
|
100
|
+
# /my/path/to/file.json
|
101
|
+
# or
|
102
|
+
# '{ "units":[ { "name":"zip", "version":"9.0", "inclusion":"false" } ] }')
|
103
|
+
json_string = ::File.exist?(::File.expand_path(val)) ? super(val) : val
|
104
|
+
::JSON.parse(json_string)
|
105
|
+
|
106
|
+
rescue ::JSON::ParserError => e
|
107
|
+
raise ArgumentError, _("Unable to parse JSON input")
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
93
112
|
|
94
113
|
class Enum < AbstractNormalizer
|
95
114
|
|
@@ -98,14 +117,14 @@ module HammerCLI
|
|
98
117
|
end
|
99
118
|
|
100
119
|
def description
|
101
|
-
"One of %s" % quoted_values
|
120
|
+
_("One of %s") % quoted_values
|
102
121
|
end
|
103
122
|
|
104
123
|
def format(value)
|
105
124
|
if @allowed_values.include? value
|
106
125
|
value
|
107
126
|
else
|
108
|
-
raise ArgumentError, "value must be one of '%s'" % quoted_values
|
127
|
+
raise ArgumentError, _("value must be one of '%s'") % quoted_values
|
109
128
|
end
|
110
129
|
end
|
111
130
|
|
@@ -120,6 +139,22 @@ module HammerCLI
|
|
120
139
|
end
|
121
140
|
end
|
122
141
|
|
142
|
+
|
143
|
+
class DateTime < AbstractNormalizer
|
144
|
+
|
145
|
+
def description
|
146
|
+
_("Date and time in YYYY-MM-DD HH:MM:SS or ISO 8601 format")
|
147
|
+
end
|
148
|
+
|
149
|
+
def format(date)
|
150
|
+
raise ArgumentError unless date
|
151
|
+
::DateTime.parse(date).to_s
|
152
|
+
rescue ArgumentError
|
153
|
+
raise ArgumentError, _("'%s' is not a valid date") % date
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
|
123
158
|
end
|
124
159
|
end
|
125
160
|
end
|
@@ -59,8 +59,8 @@ module HammerCLI
|
|
59
59
|
].compact
|
60
60
|
|
61
61
|
str = ""
|
62
|
-
str += "Can be specified multiple times. " if multivalued?
|
63
|
-
str += "Default: " + default_sources.join(", or ") unless default_sources.empty?
|
62
|
+
str += _("Can be specified multiple times. ") if multivalued?
|
63
|
+
str += _("Default: ") + default_sources.join(_(", or ")) unless default_sources.empty?
|
64
64
|
str
|
65
65
|
end
|
66
66
|
|
data/lib/hammer_cli/output.rb
CHANGED
@@ -5,4 +5,5 @@ require File.join(File.dirname(__FILE__), 'output/adapter')
|
|
5
5
|
require File.join(File.dirname(__FILE__), 'output/definition')
|
6
6
|
require File.join(File.dirname(__FILE__), 'output/dsl')
|
7
7
|
require File.join(File.dirname(__FILE__), 'output/record_collection')
|
8
|
+
require File.join(File.dirname(__FILE__), 'output/field_filter')
|
8
9
|
|