cmdb 3.0.0rc1 → 3.0.0rc2
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/Dockerfile +13 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/TODO.md +10 -0
- data/bin/console +1 -1
- data/bin/setup +1 -1
- data/bin/shell +23 -0
- data/exe/cmdb +45 -12
- data/lib/cmdb/commands/shell.rb +19 -10
- data/lib/cmdb/commands/shim.rb +9 -40
- data/lib/cmdb/interface.rb +18 -14
- data/lib/cmdb/shell/dsl.rb +14 -5
- data/lib/cmdb/shell/printer.rb +9 -19
- data/lib/cmdb/shell/prompter.rb +40 -0
- data/lib/cmdb/shell/text.rb +3 -2
- data/lib/cmdb/shell.rb +8 -0
- data/lib/cmdb/source/consul.rb +3 -2
- data/lib/cmdb/source/file.rb +5 -2
- data/lib/cmdb/source/memory.rb +1 -1
- data/lib/cmdb/source/network.rb +10 -3
- data/lib/cmdb/source.rb +2 -0
- data/lib/cmdb/version.rb +1 -1
- data/lib/cmdb.rb +8 -6
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a5fa442a4ff4b6f1d7955c804b79bce26e232a5
|
4
|
+
data.tar.gz: 60a350561cc2612639f53f41b1427b6d0ace23b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 949bf77549aefdbdb499e4d9ab818d826f5359d026dd8bd4bf6f0046a809e69e54369cb975962c3ef0c367a8ab5e3106731050dacf71eec5149fc109fec4a6a7
|
7
|
+
data.tar.gz: f5e1c629aa3135b333c89cffa7e2f1d1a4a05266c4be9fea75e3ba0cc5751c1f89c4f70d4859b464b205bb570ff67b0fb26cb898b925fa7896ccf3e96da99a53
|
data/Dockerfile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
FROM ruby:2-alpine
|
2
|
+
MAINTAINER Tony Spataro <tony@rightscale.com>
|
3
|
+
|
4
|
+
WORKDIR /cmdb
|
5
|
+
ENTRYPOINT ["bin/shell"]
|
6
|
+
|
7
|
+
# HACK: install runtime dependencies by hand in order to avoid depending on
|
8
|
+
# bundler + git + make + gcc at build time.
|
9
|
+
RUN gem install trollop -v '~> 2.0'
|
10
|
+
|
11
|
+
ADD bin /cmdb/bin/
|
12
|
+
ADD exe /cmdb/exe/
|
13
|
+
ADD lib /cmdb/lib/
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -29,7 +29,7 @@ CMDB has three primary interfaces:
|
|
29
29
|
|
30
30
|
1. The `cmdb shim` command populates the environment with values and/or rewrites hardcoded
|
31
31
|
config files, then spawns your application.
|
32
|
-
2. The `CMDB::Interface` object provides a programmatic API for querying CMDBs. Its `#
|
32
|
+
2. The `CMDB::Interface` object provides a programmatic API for querying CMDBs. Its `#to_env`
|
33
33
|
method transforms the whole configuration into an environment-friendly hash if you prefer to seed the
|
34
34
|
environment yourself, without using the shim.
|
35
35
|
3. The `cmdb shell` command navigates your k/v store using filesystem-like
|
data/Rakefile
CHANGED
data/TODO.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
1) Move array validation stuff (same type, etc) out of File source so Consul
|
2
|
+
can use it. Or decide to ditch this constraint ... hmm ...
|
3
|
+
|
4
|
+
2) Create a Shell.new helper to enable embedding of shells into other apps
|
5
|
+
|
6
|
+
3) Extract padding/truncation/excerpting code into Text module; deal with
|
7
|
+
corner cases better.
|
8
|
+
|
9
|
+
4) Move CMDB separator logic (join/split) into toplevel methods of CMDB;
|
10
|
+
DRY out Shell#expand_path
|
data/bin/console
CHANGED
data/bin/setup
CHANGED
data/bin/shell
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
|
3
|
+
tcptest() {
|
4
|
+
nc -w 1 $1 $2 < /dev/null > /dev/null 2> /dev/null
|
5
|
+
}
|
6
|
+
|
7
|
+
if [ $# -gt 0 ]; then
|
8
|
+
args="$@"
|
9
|
+
elif tcptest consul 8500; then
|
10
|
+
args="--source=consul://consul"
|
11
|
+
elif tcptest localhost 8500; then
|
12
|
+
args="--source=consul://localhost"
|
13
|
+
else
|
14
|
+
echo 'cmdb: No k/v source detected; running shell demo with fake source!'
|
15
|
+
args="--source=memory:"
|
16
|
+
fi
|
17
|
+
|
18
|
+
if gem list -i pry > /dev/null; then
|
19
|
+
pry="-rpry"
|
20
|
+
fi
|
21
|
+
|
22
|
+
echo "+ cmdb $args"
|
23
|
+
exec ruby -Ilib $pry exe/cmdb $args shell
|
data/exe/cmdb
CHANGED
@@ -25,7 +25,7 @@ command_info = command_list.map { |c| " * #{c}" }.join("\n")
|
|
25
25
|
|
26
26
|
# Use a Trollop parser for help/banner display, but do not actually parse
|
27
27
|
# anything just yet.
|
28
|
-
|
28
|
+
parser = Trollop::Parser.new do
|
29
29
|
version "cmdb #{gemspec_version} (c) 2013-2014 RightScale, Inc."
|
30
30
|
banner <<-EOS
|
31
31
|
A command-line interface for configuration management.
|
@@ -54,13 +54,22 @@ Common options that apply to all commands:
|
|
54
54
|
stop_on commands.keys
|
55
55
|
end
|
56
56
|
|
57
|
-
|
57
|
+
subcommand = nil
|
58
|
+
options = nil
|
59
|
+
|
60
|
+
Trollop.with_standard_exception_handling parser do
|
58
61
|
raise Trollop::HelpNeeded if ARGV.empty?
|
59
62
|
|
60
63
|
# Apply global options
|
61
|
-
options =
|
64
|
+
options = parser.parse ARGV
|
62
65
|
CMDB.log.level = Logger::FATAL if options[:quiet]
|
63
66
|
|
67
|
+
# Identify the subcommand and run it (or print help).
|
68
|
+
raise Trollop::HelpNeeded if ARGV.empty?
|
69
|
+
subcommand = ARGV.shift
|
70
|
+
end
|
71
|
+
|
72
|
+
begin
|
64
73
|
if options[:source].empty?
|
65
74
|
sources = CMDB::Source.detect
|
66
75
|
else
|
@@ -69,15 +78,39 @@ Trollop.with_standard_exception_handling p do
|
|
69
78
|
end
|
70
79
|
end
|
71
80
|
|
72
|
-
|
73
|
-
|
74
|
-
# Identify the subcommand and run it (or print help).
|
75
|
-
raise Trollop::HelpNeeded if ARGV.empty?
|
76
|
-
cmd = ARGV.shift
|
77
|
-
klass = commands[cmd]
|
78
|
-
if klass
|
79
|
-
klass.create(interface).run
|
80
|
-
else
|
81
|
+
klass = commands[subcommand]
|
82
|
+
unless klass
|
81
83
|
CMDB.log.fatal "Unrecognized command '#{cmd}'; try 'cmdb --help'"
|
84
|
+
exit 100
|
85
|
+
end
|
86
|
+
|
87
|
+
interface = CMDB::Interface.new(*sources)
|
88
|
+
klass.create(interface).run
|
89
|
+
rescue CMDB::BadKey => e
|
90
|
+
CMDB.log.fatal "CMDB: Bad Key: malformed CMDB key '#{e.key}'"
|
91
|
+
exit 1
|
92
|
+
rescue CMDB::BadValue => e
|
93
|
+
CMDB.log.fatal "CMDB: Bad Value: illegal value for CMDB key '#{e.key}' in source #{e.url}"
|
94
|
+
exit 2
|
95
|
+
rescue CMDB::BadData => e
|
96
|
+
CMDB.log.fatal "CMDB: Bad Data: malformed CMDB data in source #{e.url}"
|
97
|
+
exit 3
|
98
|
+
rescue CMDB::ValueConflict => e
|
99
|
+
CMDB.log.fatal "CMDB: Value Conflict: #{e.message}"
|
100
|
+
e.sources.each do |s|
|
101
|
+
CMDB.log.fatal " - #{s.url}"
|
102
|
+
end
|
103
|
+
exit 4
|
104
|
+
rescue CMDB::NameConflict => e
|
105
|
+
CMDB.log.fatal "CMDB: Name Conflict: #{e.message}"
|
106
|
+
e.keys.each do |k|
|
107
|
+
CMDB.log.fatal " - #{k}"
|
82
108
|
end
|
109
|
+
exit 4
|
110
|
+
rescue CMDB::EnvironmentConflict => e
|
111
|
+
CMDB.log.fatal "CMDB: Environment Conflict: #{e.message}"
|
112
|
+
exit 5
|
113
|
+
rescue Errno::ENOENT => e
|
114
|
+
CMDB.log.fatal "CMDB: missing file or directory #{e.message}"
|
115
|
+
exit 6
|
83
116
|
end
|
data/lib/cmdb/commands/shell.rb
CHANGED
@@ -29,6 +29,9 @@ cmdb shell
|
|
29
29
|
# @return [CMDB::Interface]
|
30
30
|
attr_reader :cmdb
|
31
31
|
|
32
|
+
# @return [CMDB::Shell::DSL]
|
33
|
+
attr_reader :dsl
|
34
|
+
|
32
35
|
# @return [Array] the "present working directory" (i.e. key prefix) for shell commands
|
33
36
|
attr_accessor :pwd
|
34
37
|
|
@@ -39,7 +42,9 @@ cmdb shell
|
|
39
42
|
def initialize(interface)
|
40
43
|
@cmdb = interface
|
41
44
|
@pwd = []
|
42
|
-
|
45
|
+
text = CMDB::Shell::Text.new(!$stdout.tty? || ENV['TERM'].nil?)
|
46
|
+
@out = CMDB::Shell::Printer.new($stdout, $stderr, text)
|
47
|
+
@in = CMDB::Shell::Prompter.new(text)
|
43
48
|
end
|
44
49
|
|
45
50
|
# Run the shim.
|
@@ -58,8 +63,6 @@ cmdb shell
|
|
58
63
|
#
|
59
64
|
# @return [String] absolute key in dotted notation
|
60
65
|
def expand_path(subpath)
|
61
|
-
pieces = subpath.split(ALT_SEPARATOR)
|
62
|
-
|
63
66
|
if subpath[0] == ALT_SEPARATOR
|
64
67
|
result = []
|
65
68
|
else
|
@@ -69,6 +72,7 @@ cmdb shell
|
|
69
72
|
if subpath.include?(ALT_SEPARATOR) || subpath =~ NAVIGATION
|
70
73
|
# filesystem-like subpath
|
71
74
|
# apply Unix-style directory navigation shortcuts
|
75
|
+
pieces = subpath.split(ALT_SEPARATOR).select { |p| !p.nil? && !p.empty? }
|
72
76
|
pieces.each do |piece|
|
73
77
|
case piece
|
74
78
|
when '..' then result.pop
|
@@ -79,6 +83,7 @@ cmdb shell
|
|
79
83
|
|
80
84
|
result.join(CMDB::SEPARATOR)
|
81
85
|
else
|
86
|
+
pieces = subpath.split(CMDB::SEPARATOR).select { |p| !p.nil? && !p.empty? }
|
82
87
|
# standard dotted notation
|
83
88
|
result += pieces
|
84
89
|
end
|
@@ -89,8 +94,7 @@ cmdb shell
|
|
89
94
|
private
|
90
95
|
|
91
96
|
def repl
|
92
|
-
|
93
|
-
while line = Readline.readline(@out.prompt(self), true)
|
97
|
+
while line = @in.read(self)
|
94
98
|
begin
|
95
99
|
line = line.chomp
|
96
100
|
next if line.nil? || line.empty?
|
@@ -100,6 +104,8 @@ cmdb shell
|
|
100
104
|
run_ruby(command, args) || run_getter(line) || run_setter(line) ||
|
101
105
|
fail(CMDB::BadCommand.new(command))
|
102
106
|
handle_output(self._)
|
107
|
+
rescue SystemCallError => e
|
108
|
+
handle_error(e) || raise
|
103
109
|
rescue => e
|
104
110
|
handle_error(e) || raise
|
105
111
|
end
|
@@ -112,7 +118,7 @@ cmdb shell
|
|
112
118
|
def run_ruby(command, args)
|
113
119
|
self._ = @dsl.__send__(command, *args)
|
114
120
|
true
|
115
|
-
rescue
|
121
|
+
rescue CMDB::BadCommand
|
116
122
|
false
|
117
123
|
rescue ArgumentError => e
|
118
124
|
raise CMDB::BadCommand.new(e.message, command)
|
@@ -126,7 +132,7 @@ cmdb shell
|
|
126
132
|
else
|
127
133
|
false
|
128
134
|
end
|
129
|
-
rescue CMDB::
|
135
|
+
rescue CMDB::BadKey
|
130
136
|
false
|
131
137
|
end
|
132
138
|
|
@@ -138,14 +144,12 @@ cmdb shell
|
|
138
144
|
value = nil unless value && value.length > 0
|
139
145
|
self._ = @dsl.set(key, value)
|
140
146
|
true
|
141
|
-
rescue CMDB::Error
|
142
|
-
false
|
143
147
|
end
|
144
148
|
|
145
149
|
def handle_output(obj)
|
146
150
|
case obj
|
147
151
|
when Hash
|
148
|
-
@out.keys_values(obj)
|
152
|
+
@out.keys_values(obj, prefix:pwd.join('.'))
|
149
153
|
else
|
150
154
|
@out.value(obj)
|
151
155
|
end
|
@@ -157,6 +161,11 @@ cmdb shell
|
|
157
161
|
when CMDB::BadCommand
|
158
162
|
@out.error "#{e.command}: #{e.message}"
|
159
163
|
true
|
164
|
+
when CMDB::Error
|
165
|
+
@out.error e.message
|
166
|
+
true
|
167
|
+
when SystemCallError
|
168
|
+
@out.error "#{e.class.name}: #{e.message}"
|
160
169
|
else
|
161
170
|
false
|
162
171
|
end
|
data/lib/cmdb/commands/shim.rb
CHANGED
@@ -15,10 +15,9 @@ option to tell the shim where to rewrite configuration files. It will look for t
|
|
15
15
|
in JSON or YML that look like <<cmdb.key.name>> and replace them with the value of
|
16
16
|
the specified key.
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
represented as DATABASE_HOST.
|
18
|
+
The shim populates the environment with all data from every source. The source-
|
19
|
+
specific prefix of each key is omitted from the environment variable name,
|
20
|
+
e.g. "common.database.host" is represented as DATABASE_HOST.
|
22
21
|
|
23
22
|
Usage:
|
24
23
|
cmdb shim [options] -- <command_to_exec> [options_for_command]
|
@@ -31,9 +30,6 @@ Where [options] are selected from:
|
|
31
30
|
opt :pretend,
|
32
31
|
'Check for errors, but do not actually launch the app or rewrite files',
|
33
32
|
default: false
|
34
|
-
opt :env,
|
35
|
-
"Add CMDB keys to the app server's process environment",
|
36
|
-
default: false
|
37
33
|
opt :user,
|
38
34
|
'Switch to named user before executing app',
|
39
35
|
type: :string
|
@@ -75,42 +71,15 @@ Where [options] are selected from:
|
|
75
71
|
#
|
76
72
|
# @raise [SystemExit] if something goes wrong
|
77
73
|
def run
|
78
|
-
|
79
|
-
|
74
|
+
populate_environment
|
75
|
+
rewrote = rewrite_files
|
80
76
|
|
81
|
-
if !rewrote &&
|
82
|
-
CMDB.log.warn 'CMDB: nothing to do; please specify --dir, --
|
77
|
+
if !rewrote && !@pretend && @command.empty?
|
78
|
+
CMDB.log.warn 'CMDB: nothing to do; please specify --dir, or -- followed by a command to run'
|
83
79
|
exit 7
|
84
80
|
end
|
85
81
|
|
86
82
|
launch_app
|
87
|
-
rescue CMDB::BadKey => e
|
88
|
-
CMDB.log.fatal "CMDB: Bad Key: malformed CMDB key '#{e.key}'"
|
89
|
-
exit 1
|
90
|
-
rescue CMDB::BadValue => e
|
91
|
-
CMDB.log.fatal "CMDB: Bad Value: illegal value for CMDB key '#{e.key}' in source #{e.url}"
|
92
|
-
exit 2
|
93
|
-
rescue CMDB::BadData => e
|
94
|
-
CMDB.log.fatal "CMDB: Bad Data: malformed CMDB data in source #{e.url}"
|
95
|
-
exit 3
|
96
|
-
rescue CMDB::ValueConflict => e
|
97
|
-
CMDB.log.fatal "CMDB: Value Conflict: #{e.message}"
|
98
|
-
e.sources.each do |s|
|
99
|
-
CMDB.log.fatal " - #{s.url}"
|
100
|
-
end
|
101
|
-
exit 4
|
102
|
-
rescue CMDB::NameConflict => e
|
103
|
-
CMDB.log.fatal "CMDB: Name Conflict: #{e.message}"
|
104
|
-
e.keys.each do |k|
|
105
|
-
CMDB.log.fatal " - #{k}"
|
106
|
-
end
|
107
|
-
exit 4
|
108
|
-
rescue CMDB::EnvironmentConflict => e
|
109
|
-
CMDB.log.fatal "CMDB: Environment Conflict: #{e.message}"
|
110
|
-
exit 5
|
111
|
-
rescue Errno::ENOENT => e
|
112
|
-
CMDB.log.fatal "CMDB: missing file or directory #{e.message}"
|
113
|
-
exit 6
|
114
83
|
end
|
115
84
|
|
116
85
|
private
|
@@ -143,8 +112,7 @@ Where [options] are selected from:
|
|
143
112
|
|
144
113
|
# @return [Boolean]
|
145
114
|
def populate_environment
|
146
|
-
env = @cmdb.
|
147
|
-
|
115
|
+
env = @cmdb.to_env
|
148
116
|
env.keys.each do |k|
|
149
117
|
raise CMDB::EnvironmentConflict.new(k) if ENV.key?(k)
|
150
118
|
end
|
@@ -153,6 +121,7 @@ Where [options] are selected from:
|
|
153
121
|
false
|
154
122
|
else
|
155
123
|
env.each_pair do |k, v|
|
124
|
+
raise "WOOGA #{k} #{v}" if k.kind_of?(Array) || v.kind_of?(Array)
|
156
125
|
ENV[k] = v
|
157
126
|
end
|
158
127
|
true
|
data/lib/cmdb/interface.rb
CHANGED
@@ -78,7 +78,7 @@ module CMDB
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def search(prefix)
|
81
|
-
prefix = Regexp.new('^' + Regexp.escape(prefix))
|
81
|
+
prefix = Regexp.new('^' + Regexp.escape(prefix)) unless prefix.is_a?(Regexp)
|
82
82
|
result = {}
|
83
83
|
|
84
84
|
@sources.each do |s|
|
@@ -101,19 +101,19 @@ module CMDB
|
|
101
101
|
# rather than returning bad data.
|
102
102
|
#
|
103
103
|
# @raise [NameConflict] if two or more key names transform to the same
|
104
|
-
def
|
104
|
+
def to_env
|
105
105
|
values = {}
|
106
|
-
|
106
|
+
originals = {}
|
107
107
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
108
|
+
@sources.each do |s|
|
109
|
+
s.each_pair do |k, v|
|
110
|
+
env = key_to_env(k, s)
|
111
|
+
if (orig = originals[env])
|
112
|
+
raise NameConflict.new(env, [orig, k])
|
113
|
+
else
|
114
|
+
values[env] = value_to_env(v)
|
115
|
+
originals[env] = k
|
116
|
+
end
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
@@ -135,8 +135,12 @@ module CMDB
|
|
135
135
|
end
|
136
136
|
|
137
137
|
# Make an environment variable out of a key name
|
138
|
-
def key_to_env(key)
|
139
|
-
|
138
|
+
def key_to_env(key, source)
|
139
|
+
if source.prefix
|
140
|
+
env_name = key[source.prefix.size+1..-1]
|
141
|
+
else
|
142
|
+
env_name = key.dup
|
143
|
+
end
|
140
144
|
env_name.gsub!(/[^A-Za-z0-9_]+/, '_')
|
141
145
|
env_name.upcase!
|
142
146
|
env_name
|
data/lib/cmdb/shell/dsl.rb
CHANGED
@@ -10,6 +10,14 @@ module CMDB::Shell
|
|
10
10
|
@out = out
|
11
11
|
end
|
12
12
|
|
13
|
+
def class
|
14
|
+
DSL
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(meth, *args)
|
18
|
+
::Kernel.raise ::CMDB::BadCommand.new(meth)
|
19
|
+
end
|
20
|
+
|
13
21
|
def ls(path='')
|
14
22
|
prefix = @shell.expand_path(path)
|
15
23
|
@cmdb.search prefix
|
@@ -17,15 +25,16 @@ module CMDB::Shell
|
|
17
25
|
|
18
26
|
def help
|
19
27
|
@out.info 'Commands:'
|
20
|
-
@out.info ' cd
|
21
|
-
@out.info ' cd /
|
28
|
+
@out.info ' cd <key> - append/remove search prefix'
|
29
|
+
@out.info ' cd / - reset search prefix'
|
22
30
|
@out.info ' get <key> - print value of key'
|
23
31
|
@out.info ' ls - show keys and values'
|
24
32
|
@out.info ' set <key> <value> - print value of key'
|
25
33
|
@out.info ' quit - exit the shell'
|
26
|
-
@out.info '
|
27
|
-
@out.info ' a.b.c -
|
28
|
-
@out.info ' ../b/c -
|
34
|
+
@out.info 'Key notation:'
|
35
|
+
@out.info ' a.b.c - relative to search prefix'
|
36
|
+
@out.info ' ../b/c - relative to parent of search prefix'
|
37
|
+
@out.info ' /a - relative to root (i.e. all sources)'
|
29
38
|
@out.info 'Shortcuts:'
|
30
39
|
@out.info ' <key> - for get'
|
31
40
|
@out.info ' <key>=<value> - for set'
|
data/lib/cmdb/shell/printer.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module CMDB::Shell
|
2
2
|
class Printer
|
3
|
-
def initialize(out
|
3
|
+
def initialize(out=$stdout, err=$stderr, text=Text.new(true))
|
4
4
|
@out = out
|
5
5
|
@err = err
|
6
|
-
@c =
|
6
|
+
@c = text
|
7
7
|
end
|
8
8
|
|
9
9
|
# Print an informational message.
|
10
10
|
def info(str)
|
11
11
|
@out.puts @c.white(str)
|
12
|
+
self
|
12
13
|
end
|
13
14
|
|
14
15
|
# Print an error message.
|
@@ -19,7 +20,7 @@ module CMDB::Shell
|
|
19
20
|
|
20
21
|
# Display a single CMDB value.
|
21
22
|
def value(obj)
|
22
|
-
@out.puts ' ' + color_value(obj,
|
23
|
+
@out.puts ' ' + color_value(obj, @c.width-2)
|
23
24
|
self
|
24
25
|
end
|
25
26
|
|
@@ -27,36 +28,26 @@ module CMDB::Shell
|
|
27
28
|
def keys_values(h, prefix:nil)
|
28
29
|
wk = h.keys.inject(0) { |ax, e| e.size > ax ? e.size : ax }
|
29
30
|
wv = h.values.inject(0) { |ax, e| es = e.inspect.size; es > ax ? es : ax }
|
30
|
-
|
31
|
-
|
32
|
-
wk = [wk, half].min
|
31
|
+
half = @c.width / 2
|
32
|
+
wk = [wk, half].min-3
|
33
33
|
wv = [wv, half].min
|
34
|
-
re = (width - wk - wv)
|
34
|
+
re = (@c.width - wk - wv)
|
35
35
|
wv += re if re > 0
|
36
36
|
|
37
37
|
h.each do |k, v|
|
38
|
-
@out.puts format(' %s%s', color_key(k, wk+1, prefix:prefix), color_value(v, wv))
|
38
|
+
@out.puts format(' %s %s', color_key(k, wk+1, prefix:prefix), color_value(v, wv))
|
39
39
|
end
|
40
40
|
|
41
41
|
self
|
42
42
|
end
|
43
43
|
|
44
|
-
# @return [String] human-readable CMDB prompt
|
45
|
-
def prompt(cmdb)
|
46
|
-
pwd = '/' + cmdb.pwd.join('/')
|
47
|
-
pwd = pwd[0...40] + '...' if pwd.size >= 40
|
48
|
-
'cmdb:' +
|
49
|
-
@c.green(pwd) +
|
50
|
-
@c.default('> ')
|
51
|
-
end
|
52
|
-
|
53
44
|
private
|
54
45
|
|
55
46
|
# Colorize a key and right-pad it to fit a minimum size. Append a ':'
|
56
47
|
# to make it YAML-esque.
|
57
48
|
def color_key(k, size, prefix:nil)
|
58
49
|
v = k.to_s
|
59
|
-
v.sub(prefix, '') if prefix && v.index(prefix) == 0
|
50
|
+
v = v.sub(prefix, '') if prefix && v.index(prefix) == 0
|
60
51
|
suffix = ':'
|
61
52
|
if v.size + 1 > size
|
62
53
|
v = v[0...size-4]
|
@@ -76,7 +67,6 @@ module CMDB::Shell
|
|
76
67
|
else
|
77
68
|
vv = v.inspect
|
78
69
|
end
|
79
|
-
vv << (' ' * [0, size - vv.size].max)
|
80
70
|
|
81
71
|
case v
|
82
72
|
when Symbol
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module CMDB::Shell
|
2
|
+
class Prompter
|
3
|
+
def initialize(text)
|
4
|
+
require 'readline'
|
5
|
+
@c = text
|
6
|
+
end
|
7
|
+
|
8
|
+
# Prompt the user for input and read a line. Offer autocompletion services
|
9
|
+
# using Readline and CMDB/DSL searches.
|
10
|
+
def read(shell)
|
11
|
+
Readline.completion_proc = proc do |word|
|
12
|
+
commands = shell.dsl.class.instance_methods.select do |m|
|
13
|
+
m.to_s.index(word) == 0 && m !~ /[^a-z]/
|
14
|
+
end.map(&:to_s)
|
15
|
+
next commands if commands.any?
|
16
|
+
|
17
|
+
hits = shell.cmdb.search(shell.expand_path(word)).keys
|
18
|
+
hits.sort! { |x, y| x.size <=> y.size }
|
19
|
+
pwd = shell.pwd.join('.')
|
20
|
+
hits[0...CMDB::Shell::MANY].map { |k| k.sub(pwd, '') }
|
21
|
+
end
|
22
|
+
|
23
|
+
Readline.readline(prompt(shell.cmdb, shell.pwd), true)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# Return a suitable prompt for later printing.
|
29
|
+
#
|
30
|
+
# @return [String] human-readable CMDB prompt
|
31
|
+
def prompt(cmdb, pwd)
|
32
|
+
max=@c.width/2
|
33
|
+
pwd = '/' + pwd.join('/')
|
34
|
+
pwd = '...' + pwd[-max..-1] if pwd.size >= max
|
35
|
+
'cmdb:' +
|
36
|
+
@c.green(pwd) +
|
37
|
+
@c.default('> ')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/cmdb/shell/text.rb
CHANGED
@@ -15,6 +15,7 @@ module CMDB::Shell
|
|
15
15
|
|
16
16
|
def initialize(plain)
|
17
17
|
@plain = plain
|
18
|
+
trap('SIGWINCH') { @width = nil } unless @plain
|
18
19
|
end
|
19
20
|
|
20
21
|
COLORS.each_pair do |color, value|
|
@@ -54,11 +55,11 @@ module CMDB::Shell
|
|
54
55
|
alias_method :bright_default, :bold
|
55
56
|
|
56
57
|
# @return [Integer] screen width (number of columns)
|
57
|
-
def
|
58
|
+
def width
|
58
59
|
if @plain
|
59
60
|
65_535
|
60
61
|
else
|
61
|
-
Integer(`stty size`.chomp.split(/ +/)[1]) rescue 80
|
62
|
+
@width ||= Integer(`stty size`.chomp.split(/ +/)[1]) rescue 80
|
62
63
|
end
|
63
64
|
end
|
64
65
|
end
|
data/lib/cmdb/shell.rb
CHANGED
@@ -1,8 +1,16 @@
|
|
1
1
|
module CMDB
|
2
2
|
module Shell
|
3
|
+
# Maximum number of "things" before the UI starts collapsing display
|
4
|
+
# elements, i.e. treating subtrees as subdirectories.
|
5
|
+
FEW = 3
|
6
|
+
|
7
|
+
# Maximum number of "things" for which to offer autocomplete and other
|
8
|
+
# shortcuts.
|
9
|
+
MANY = 25
|
3
10
|
end
|
4
11
|
end
|
5
12
|
|
6
13
|
require 'cmdb/shell/dsl'
|
7
14
|
require 'cmdb/shell/text'
|
8
15
|
require 'cmdb/shell/printer'
|
16
|
+
require 'cmdb/shell/prompter'
|
data/lib/cmdb/source/consul.rb
CHANGED
@@ -20,11 +20,11 @@ module CMDB
|
|
20
20
|
when String
|
21
21
|
response = json_parse(response)
|
22
22
|
item = response.first
|
23
|
-
json_parse(Base64.decode64(item['Value']))
|
23
|
+
item['Value'] && json_parse(Base64.decode64(item['Value']))
|
24
24
|
when 404
|
25
25
|
nil
|
26
26
|
else
|
27
|
-
raise CMDB
|
27
|
+
raise CMDB::Error.new("Unexpected consul response #{response.inspect}")
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
@@ -60,6 +60,7 @@ module CMDB
|
|
60
60
|
|
61
61
|
result.each do |item|
|
62
62
|
key = slash_to_dot(item['Key'])
|
63
|
+
next unless item['Value']
|
63
64
|
value = json_parse(Base64.decode64(item['Value']))
|
64
65
|
yield(key, value)
|
65
66
|
end
|
data/lib/cmdb/source/file.rb
CHANGED
@@ -64,14 +64,17 @@ module CMDB
|
|
64
64
|
when Hash
|
65
65
|
flatten(value, key, output)
|
66
66
|
when Array
|
67
|
-
if value.
|
67
|
+
if value.any? { |e| e.is_a?(Hash) }
|
68
|
+
# mismatched arrays: not allowed
|
69
|
+
raise BadValue.new(url, key, value, 'hashes not allowed inside arrays')
|
70
|
+
elsif value.all? { |e| e.is_a?(String) } ||
|
68
71
|
value.all? { |e| e.is_a?(Numeric) } ||
|
69
72
|
value.all? { |e| e == true } ||
|
70
73
|
value.all? { |e| e == false }
|
71
74
|
output[key] = value
|
72
75
|
else
|
73
76
|
# mismatched arrays: not allowed
|
74
|
-
raise BadValue.new(url, key, value)
|
77
|
+
raise BadValue.new(url, key, value, 'mismatched/unsupported element types')
|
75
78
|
end
|
76
79
|
when String, Numeric, TrueClass, FalseClass
|
77
80
|
output[key] = value
|
data/lib/cmdb/source/memory.rb
CHANGED
data/lib/cmdb/source/network.rb
CHANGED
@@ -10,11 +10,12 @@ module CMDB
|
|
10
10
|
private
|
11
11
|
|
12
12
|
# Perform a GET. On 2xx, return the response body as a String.
|
13
|
-
# On 3xx
|
13
|
+
# On 3xx or 4xx return an Integer status code. On 5xx, retry up to
|
14
|
+
# the specified number of times, then return an Integer status code.
|
14
15
|
#
|
15
16
|
# @param [String] path
|
16
17
|
# @return [Integer,String]
|
17
|
-
def http_get(path, query:nil)
|
18
|
+
def http_get(path, query:nil, retries:3)
|
18
19
|
@http ||= Net::HTTP.start(@uri.host, @uri.port)
|
19
20
|
uri = @uri.dup
|
20
21
|
uri.path = path
|
@@ -26,8 +27,14 @@ module CMDB
|
|
26
27
|
case response.code.to_i
|
27
28
|
when 200..299
|
28
29
|
response.body
|
30
|
+
when 500..599
|
31
|
+
if retries > 0
|
32
|
+
http_get(path, query:query, retries:retries-1)
|
33
|
+
else
|
34
|
+
response.code.to_i
|
35
|
+
end
|
29
36
|
else
|
30
|
-
|
37
|
+
response.code.to_i
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
data/lib/cmdb/source.rb
CHANGED
data/lib/cmdb/version.rb
CHANGED
data/lib/cmdb.rb
CHANGED
@@ -39,7 +39,7 @@ module CMDB
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
# A value of an unsupported type was encountered in the CMDB.
|
42
|
+
# A value of an unsupported type was encountered in the CMDB or filesystem.
|
43
43
|
class BadValue < Error
|
44
44
|
# @return [URI] filesystem or network location of the bad value
|
45
45
|
attr_reader :url
|
@@ -50,14 +50,16 @@ module CMDB
|
|
50
50
|
# @param [URI] url filesystem or network location of the bad value
|
51
51
|
# @param [String] key CMDB key name under which the bad value was found
|
52
52
|
# @param [Object] value the bad value itself
|
53
|
-
def initialize(url, key, value)
|
53
|
+
def initialize(url, key, value, desc=nil)
|
54
54
|
@url = url
|
55
55
|
@key = key
|
56
|
-
|
56
|
+
msg = "Unsupported #{value.class.name} value"
|
57
|
+
msg << ": #{desc}" if desc
|
58
|
+
super(msg)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
|
60
|
-
# Malformed data was encountered in the CMDB or
|
62
|
+
# Malformed data was encountered in the CMDB or filesystem.
|
61
63
|
class BadData < Error
|
62
64
|
# @return [URI] filesystem or network location of the bad data
|
63
65
|
attr_reader :url
|
@@ -94,7 +96,7 @@ module CMDB
|
|
94
96
|
# Deprecated name for ValueConflict
|
95
97
|
Conflict = ValueConflict
|
96
98
|
|
97
|
-
# Two or more keys in
|
99
|
+
# Two or more keys in prefixed sources have an identical name. This isn't an error
|
98
100
|
# when CMDB is used to refer to keys by their full, prefixed name, but it can become
|
99
101
|
# an issue when loading keys into the environment for 12-factor apps to process.
|
100
102
|
class NameConflict < Error
|
@@ -127,7 +129,7 @@ module CMDB
|
|
127
129
|
|
128
130
|
def log
|
129
131
|
unless @log
|
130
|
-
@log = Logger.new(
|
132
|
+
@log = Logger.new($stderr)
|
131
133
|
|
132
134
|
@log.formatter = Proc.new do |severity, datetime, progname, msg|
|
133
135
|
"#{severity}: #{msg}\n"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cmdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.
|
4
|
+
version: 3.0.0rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RightScale
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: trollop
|
@@ -52,13 +52,16 @@ files:
|
|
52
52
|
- ".rubocop.yml"
|
53
53
|
- ".travis.yml"
|
54
54
|
- CHANGELOG.md
|
55
|
+
- Dockerfile
|
55
56
|
- Gemfile
|
56
57
|
- Gemfile.lock
|
57
58
|
- LICENSE
|
58
59
|
- README.md
|
59
60
|
- Rakefile
|
61
|
+
- TODO.md
|
60
62
|
- bin/console
|
61
63
|
- bin/setup
|
64
|
+
- bin/shell
|
62
65
|
- cmdb.gemspec
|
63
66
|
- docker-compose.yml
|
64
67
|
- exe/cmdb
|
@@ -76,6 +79,7 @@ files:
|
|
76
79
|
- lib/cmdb/shell.rb
|
77
80
|
- lib/cmdb/shell/dsl.rb
|
78
81
|
- lib/cmdb/shell/printer.rb
|
82
|
+
- lib/cmdb/shell/prompter.rb
|
79
83
|
- lib/cmdb/shell/text.rb
|
80
84
|
- lib/cmdb/source.rb
|
81
85
|
- lib/cmdb/source/consul.rb
|