cmdb 2.6.2 → 3.0.0rc1
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/.dockerignore +7 -0
- data/.gitignore +3 -0
- data/CHANGELOG.md +71 -34
- data/Gemfile +4 -3
- data/Gemfile.lock +6 -19
- data/LICENSE +25 -0
- data/README.md +138 -166
- data/Rakefile +13 -0
- data/cmdb.gemspec +1 -3
- data/docker-compose.yml +15 -0
- data/exe/cmdb +36 -9
- data/lib/cmdb/commands/help.rb +21 -66
- data/lib/cmdb/commands/shell.rb +165 -0
- data/lib/cmdb/commands/shim.rb +13 -128
- data/lib/cmdb/commands.rb +1 -0
- data/lib/cmdb/interface.rb +51 -59
- data/lib/cmdb/shell/dsl.rb +73 -0
- data/lib/cmdb/shell/printer.rb +115 -0
- data/lib/cmdb/shell/text.rb +65 -0
- data/lib/cmdb/shell.rb +8 -0
- data/lib/cmdb/source/consul.rb +90 -0
- data/lib/cmdb/{file_source.rb → source/file.rb} +15 -34
- data/lib/cmdb/source/memory.rb +39 -0
- data/lib/cmdb/source/network.rb +104 -0
- data/lib/cmdb/source.rb +94 -0
- data/lib/cmdb/version.rb +1 -1
- data/lib/cmdb.rb +26 -17
- metadata +18 -36
- data/TODO.md +0 -3
- data/lib/cmdb/consul_source.rb +0 -83
@@ -0,0 +1,104 @@
|
|
1
|
+
module CMDB
|
2
|
+
class Source::Network < Source
|
3
|
+
# @param [URI] uri
|
4
|
+
# @param [String] dot-notation prefix of all keys
|
5
|
+
def initialize(uri, prefix)
|
6
|
+
@uri = uri
|
7
|
+
@prefix = prefix
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
# Perform a GET. On 2xx, return the response body as a String.
|
13
|
+
# On 3xx, 4xx, 5xx, return an Integer status code.
|
14
|
+
#
|
15
|
+
# @param [String] path
|
16
|
+
# @return [Integer,String]
|
17
|
+
def http_get(path, query:nil)
|
18
|
+
@http ||= Net::HTTP.start(@uri.host, @uri.port)
|
19
|
+
uri = @uri.dup
|
20
|
+
uri.path = path
|
21
|
+
uri.query = query unless query.nil? || query.empty?
|
22
|
+
|
23
|
+
request = Net::HTTP::Get.new uri
|
24
|
+
response = @http.request request
|
25
|
+
|
26
|
+
case response.code.to_i
|
27
|
+
when 200..299
|
28
|
+
response.body
|
29
|
+
else
|
30
|
+
return response.code.to_i
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Perform a PUT. JSON-encode request entity unless it is already a String.
|
35
|
+
# Return status code from HTTP response.
|
36
|
+
#
|
37
|
+
# @return [Integer] HTTP status code
|
38
|
+
# @param [String] path
|
39
|
+
# @param [String,Hash,Array,Numeric,Boolean] entity
|
40
|
+
def http_put(path, entity)
|
41
|
+
entity = JSON.dump(entity) unless entity.is_a?(String)
|
42
|
+
|
43
|
+
@http ||= Net::HTTP.start(@uri.host, @uri.port)
|
44
|
+
uri = @uri.dup
|
45
|
+
uri.path = path
|
46
|
+
|
47
|
+
request = Net::HTTP::Put.new uri
|
48
|
+
request.body = entity
|
49
|
+
response = @http.request request
|
50
|
+
|
51
|
+
response.code.to_i
|
52
|
+
end
|
53
|
+
|
54
|
+
# Perform a DELETE.
|
55
|
+
# Return status code from HTTP response.
|
56
|
+
#
|
57
|
+
# @return [Integer] HTTP status code
|
58
|
+
# @param [String] path
|
59
|
+
def http_delete(path)
|
60
|
+
@http ||= Net::HTTP.start(@uri.host, @uri.port)
|
61
|
+
uri = @uri.dup
|
62
|
+
uri.path = path
|
63
|
+
|
64
|
+
request = Net::HTTP::Delete.new uri
|
65
|
+
response = @http.request request
|
66
|
+
|
67
|
+
response.code.to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
# Attempt to parse str as a JSON document or fragment. Fragments include
|
71
|
+
# "bare" numbers, strings, `null`, `true`, `false` that occur outside of
|
72
|
+
# a dictionary or array context, but exclude anything that is syntactically
|
73
|
+
# invalid JSON (e.g. an unquoted string).
|
74
|
+
#
|
75
|
+
# @return [Object] JSON-parsed object, or the original string if parse fails
|
76
|
+
# @param [String] str
|
77
|
+
def json_parse(str)
|
78
|
+
JSON.load(str)
|
79
|
+
rescue JSON::ParserError
|
80
|
+
str
|
81
|
+
end
|
82
|
+
|
83
|
+
# Convert dotted notation to slash-separated notation without an initial
|
84
|
+
# slash. Remove prefix if appropriate; raise an error if the key name
|
85
|
+
# does not begin with this source's prefix.
|
86
|
+
def dot_to_slash(key)
|
87
|
+
unless prefixed?(key)
|
88
|
+
raise CMDB::BadKey.new(key, "Keys of this source must begin with #{prefix}.")
|
89
|
+
end
|
90
|
+
pieces = key.split(CMDB::SEPARATOR)
|
91
|
+
pieces.shift
|
92
|
+
pieces.join('/')
|
93
|
+
end
|
94
|
+
|
95
|
+
# Convert a slash-separated URI path or subpath to dotted notation. If there is an initial
|
96
|
+
# slash, discard it. Prepend source's prefix to key name if not already present.
|
97
|
+
def slash_to_dot(path)
|
98
|
+
pieces = path.split('/')
|
99
|
+
pieces.shift if pieces[0].empty?
|
100
|
+
pieces.unshift(prefix) unless prefix.nil? || pieces[0] == prefix
|
101
|
+
pieces.join(CMDB::SEPARATOR)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/cmdb/source.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module CMDB
|
2
|
+
class Source
|
3
|
+
# Determine the prefix of all keys provided by this source. No two sources can share
|
4
|
+
# a prefix (other than nil) and no source's prefix can be a prefix of any other
|
5
|
+
# source's prefix.
|
6
|
+
#
|
7
|
+
# Some sources have no common prefix, in which case this reader returns nil.
|
8
|
+
#
|
9
|
+
# @return [nil,String] common and unique dot-notation prefix of this source's keys, if any
|
10
|
+
attr_reader :prefix
|
11
|
+
|
12
|
+
# @return [URI] a URL describing where this source's data comes from
|
13
|
+
attr_reader :url
|
14
|
+
|
15
|
+
# Construct a source given a URI that identifies both the type of
|
16
|
+
# source (consul, file or environment) and its location if applicable.
|
17
|
+
# Choose a suitable prefix for the source based on the URI contents.
|
18
|
+
#
|
19
|
+
# If you pass a fragment as part of the URI, then the fragment becomes the
|
20
|
+
# prefix. Otherwise, the final path component becomes the prefix, unless
|
21
|
+
# it is empty in which case the first hostname component becomes the prefix.
|
22
|
+
#
|
23
|
+
# @param [String,URI] location of source
|
24
|
+
#
|
25
|
+
# @raise ArgumentError if URL scheme is unrecognized
|
26
|
+
#
|
27
|
+
# @example environment source
|
28
|
+
# CMDB::Source.create('env')
|
29
|
+
#
|
30
|
+
# @example JSON source
|
31
|
+
# CMDB::Source.create('file://awesome.json')
|
32
|
+
#
|
33
|
+
# @example YAML source
|
34
|
+
# CMDB::Source.create('file://awesome.yml')
|
35
|
+
#
|
36
|
+
# @example consul source at the root of the k/v tree with prefix "otherhost" (probably not desirable!)
|
37
|
+
# CMDB::Source.create('consul://otherhost.example.com')
|
38
|
+
#
|
39
|
+
# @example consul source at the root of the k/v tree with prefix "myapp"
|
40
|
+
# CMDB::Source.create('consul://otherhost.example.com#myapp')
|
41
|
+
#
|
42
|
+
# @example consul source whose keys are drawn from a subtree of the k/v with prefix "interesting."
|
43
|
+
# CMDB::Source.create('consul://localhost/interesting')
|
44
|
+
#
|
45
|
+
# @example consul source with nonstandard location and port and prefix "my-kv"
|
46
|
+
# CMDB::Source.create('consul://my-kv:18500')
|
47
|
+
def self.create(uri)
|
48
|
+
uri = URI.parse(uri) if uri.is_a?(String)
|
49
|
+
|
50
|
+
if !uri.path.nil? && !uri.path.empty?
|
51
|
+
prefix = ::File.basename(uri.path, '.*')
|
52
|
+
else
|
53
|
+
prefix = nil
|
54
|
+
end
|
55
|
+
|
56
|
+
case uri.scheme
|
57
|
+
when 'consul'
|
58
|
+
curi = uri.dup
|
59
|
+
curi.scheme = 'http'
|
60
|
+
curi.port ||= 8500
|
61
|
+
curi.path = ''
|
62
|
+
Source::Consul.new(URI.parse(curi.to_s), prefix)
|
63
|
+
when 'file'
|
64
|
+
Source::File.new(uri.path, prefix)
|
65
|
+
else
|
66
|
+
raise ArgumentError, "Unrecognized URL scheme '#{uri.scheme}'"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Test for the presence of some default sources and return any that exist.
|
71
|
+
#
|
72
|
+
# @return [Array] a set of initialized CMDB sources
|
73
|
+
def self.detect
|
74
|
+
sources = []
|
75
|
+
|
76
|
+
consul = create('consul://localhost')
|
77
|
+
sources << consul if consul.ping
|
78
|
+
|
79
|
+
sources
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
# Check whether a key's prefix is suitable for this source.
|
85
|
+
def prefixed?(key)
|
86
|
+
prefix.nil? || (key.index(prefix) == 0 && key[prefix.size] == '.')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
require 'cmdb/source/memory'
|
92
|
+
require 'cmdb/source/file'
|
93
|
+
require 'cmdb/source/network'
|
94
|
+
require 'cmdb/source/consul'
|
data/lib/cmdb/version.rb
CHANGED
data/lib/cmdb.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
+
require 'logger'
|
2
3
|
require 'set'
|
3
4
|
require 'singleton'
|
4
5
|
|
5
6
|
module CMDB
|
6
|
-
#
|
7
|
-
#
|
8
|
-
|
7
|
+
# Character that separates keys from subkeys in the standard notation for
|
8
|
+
# CMDB keys.
|
9
|
+
SEPARATOR = '.'.freeze
|
9
10
|
|
10
11
|
# Regexp that matches valid key names. Key names consist of one or more dot-separated words;
|
11
12
|
# each word must begin with a lowercase alpha character and may contain alphanumerics or
|
12
13
|
# underscores.
|
13
|
-
VALID_KEY = /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$/
|
14
|
+
VALID_KEY = /^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$/i.freeze
|
14
15
|
|
15
16
|
class Error < StandardError; end
|
16
17
|
|
@@ -26,15 +27,15 @@ module CMDB
|
|
26
27
|
end
|
27
28
|
end
|
28
29
|
|
29
|
-
# Client
|
30
|
+
# Client used a malformed key name.
|
30
31
|
class BadKey < Error
|
31
32
|
# @return [String] the name of the offending key
|
32
33
|
attr_reader :key
|
33
34
|
|
34
35
|
# @param [String] name
|
35
|
-
def initialize(key)
|
36
|
+
def initialize(key, message="Malformed key '#{key}'")
|
37
|
+
super(message)
|
36
38
|
@key = key
|
37
|
-
super("Malformed key '#{key}'")
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
@@ -69,6 +70,16 @@ module CMDB
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
73
|
+
# Client asked to do something that does not make sense.
|
74
|
+
class BadCommand < Error
|
75
|
+
attr_reader :command
|
76
|
+
|
77
|
+
def initialize(command, message='Unrecognized command')
|
78
|
+
super(message)
|
79
|
+
@command = command
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
72
83
|
# Two or more sources contain keys for the same namespace; this is only allowed in development
|
73
84
|
# environments.
|
74
85
|
class ValueConflict < Error
|
@@ -83,8 +94,8 @@ module CMDB
|
|
83
94
|
# Deprecated name for ValueConflict
|
84
95
|
Conflict = ValueConflict
|
85
96
|
|
86
|
-
# Two or more keys in different
|
87
|
-
# when CMDB is used to refer to keys by their full,
|
97
|
+
# Two or more keys in different sources have an identical name. This isn't an error
|
98
|
+
# when CMDB is used to refer to keys by their full, prefixed name, but it can become
|
88
99
|
# an issue when loading keys into the environment for 12-factor apps to process.
|
89
100
|
class NameConflict < Error
|
90
101
|
attr_reader :env
|
@@ -117,6 +128,11 @@ module CMDB
|
|
117
128
|
def log
|
118
129
|
unless @log
|
119
130
|
@log = Logger.new(STDOUT)
|
131
|
+
|
132
|
+
@log.formatter = Proc.new do |severity, datetime, progname, msg|
|
133
|
+
"#{severity}: #{msg}\n"
|
134
|
+
end
|
135
|
+
|
120
136
|
@log.level = Logger::WARN
|
121
137
|
end
|
122
138
|
|
@@ -126,16 +142,9 @@ module CMDB
|
|
126
142
|
def log=(log)
|
127
143
|
@log = log
|
128
144
|
end
|
129
|
-
|
130
|
-
# Determine whether CMDB is running in a development environment.
|
131
|
-
# @return [Boolean]
|
132
|
-
def development?
|
133
|
-
DEVELOPMENT_ENVIRONMENTS.include?(ENV['RACK_ENV'] || ENV['RAILS_ENV'])
|
134
|
-
end
|
135
145
|
end
|
136
146
|
|
137
|
-
require 'cmdb/
|
138
|
-
require 'cmdb/file_source'
|
147
|
+
require 'cmdb/source'
|
139
148
|
require 'cmdb/interface'
|
140
149
|
require 'cmdb/rewriter'
|
141
150
|
require 'cmdb/commands'
|
metadata
CHANGED
@@ -1,43 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cmdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0rc1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RightScale
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: listen
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '3.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '3.0'
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: diplomat
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '0.15'
|
34
|
-
type: :runtime
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '0.15'
|
41
13
|
- !ruby/object:Gem::Dependency
|
42
14
|
name: trollop
|
43
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,6 +46,7 @@ executables:
|
|
74
46
|
extensions: []
|
75
47
|
extra_rdoc_files: []
|
76
48
|
files:
|
49
|
+
- ".dockerignore"
|
77
50
|
- ".gitignore"
|
78
51
|
- ".rspec"
|
79
52
|
- ".rubocop.yml"
|
@@ -81,12 +54,13 @@ files:
|
|
81
54
|
- CHANGELOG.md
|
82
55
|
- Gemfile
|
83
56
|
- Gemfile.lock
|
57
|
+
- LICENSE
|
84
58
|
- README.md
|
85
59
|
- Rakefile
|
86
|
-
- TODO.md
|
87
60
|
- bin/console
|
88
61
|
- bin/setup
|
89
62
|
- cmdb.gemspec
|
63
|
+
- docker-compose.yml
|
90
64
|
- exe/cmdb
|
91
65
|
- fixtures/Gemfile
|
92
66
|
- fixtures/Gemfile.lock
|
@@ -95,11 +69,19 @@ files:
|
|
95
69
|
- lib/cmdb.rb
|
96
70
|
- lib/cmdb/commands.rb
|
97
71
|
- lib/cmdb/commands/help.rb
|
72
|
+
- lib/cmdb/commands/shell.rb
|
98
73
|
- lib/cmdb/commands/shim.rb
|
99
|
-
- lib/cmdb/consul_source.rb
|
100
|
-
- lib/cmdb/file_source.rb
|
101
74
|
- lib/cmdb/interface.rb
|
102
75
|
- lib/cmdb/rewriter.rb
|
76
|
+
- lib/cmdb/shell.rb
|
77
|
+
- lib/cmdb/shell/dsl.rb
|
78
|
+
- lib/cmdb/shell/printer.rb
|
79
|
+
- lib/cmdb/shell/text.rb
|
80
|
+
- lib/cmdb/source.rb
|
81
|
+
- lib/cmdb/source/consul.rb
|
82
|
+
- lib/cmdb/source/file.rb
|
83
|
+
- lib/cmdb/source/memory.rb
|
84
|
+
- lib/cmdb/source/network.rb
|
103
85
|
- lib/cmdb/version.rb
|
104
86
|
homepage: https://github.com/rightscale/cmdb
|
105
87
|
licenses:
|
@@ -113,12 +95,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
113
95
|
requirements:
|
114
96
|
- - "~>"
|
115
97
|
- !ruby/object:Gem::Version
|
116
|
-
version: '2.
|
98
|
+
version: '2.1'
|
117
99
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
100
|
requirements:
|
119
|
-
- - "
|
101
|
+
- - ">"
|
120
102
|
- !ruby/object:Gem::Version
|
121
|
-
version:
|
103
|
+
version: 1.3.1
|
122
104
|
requirements: []
|
123
105
|
rubyforge_project:
|
124
106
|
rubygems_version: 2.4.5.1
|
data/TODO.md
DELETED
data/lib/cmdb/consul_source.rb
DELETED
@@ -1,83 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require 'diplomat'
|
3
|
-
|
4
|
-
module CMDB
|
5
|
-
class ConsulSource
|
6
|
-
# Regular expression to match array values
|
7
|
-
ARRAY_VALUE = /^\[(.*)\]$/
|
8
|
-
|
9
|
-
### Class variables
|
10
|
-
|
11
|
-
class << self
|
12
|
-
attr_writer :url
|
13
|
-
end
|
14
|
-
|
15
|
-
class << self
|
16
|
-
attr_reader :url
|
17
|
-
end
|
18
|
-
|
19
|
-
class << self
|
20
|
-
attr_writer :prefixes
|
21
|
-
end
|
22
|
-
|
23
|
-
class << self
|
24
|
-
attr_reader :prefixes
|
25
|
-
end
|
26
|
-
|
27
|
-
### Instance variables
|
28
|
-
|
29
|
-
# The url to communicate with consul
|
30
|
-
@url = nil
|
31
|
-
|
32
|
-
# Initialize the configuration for consul source
|
33
|
-
def initialize(prefix)
|
34
|
-
Diplomat.configure do |config|
|
35
|
-
config.url = self.class.url
|
36
|
-
end
|
37
|
-
@prefix = prefix
|
38
|
-
end
|
39
|
-
|
40
|
-
# Get a single key from consul
|
41
|
-
def get(key)
|
42
|
-
value = Diplomat::Kv.get(dot_to_slash(key))
|
43
|
-
process_value(value)
|
44
|
-
rescue TypeError
|
45
|
-
puts 'hi'
|
46
|
-
rescue Diplomat::KeyNotFound
|
47
|
-
nil
|
48
|
-
end
|
49
|
-
|
50
|
-
# Not implemented for consul source
|
51
|
-
def each_pair(&_block)
|
52
|
-
prefix = @prefix || ''
|
53
|
-
all = Diplomat::Kv.get(prefix, recurse: true)
|
54
|
-
all.each do |item|
|
55
|
-
dotted_prefix = prefix.split('/').join('.')
|
56
|
-
dotted_key = item[:key].split('/').join('.')
|
57
|
-
key = dotted_prefix == '' ? dotted_key : dotted_key.split("#{dotted_prefix}.").last
|
58
|
-
value = process_value(item[:value])
|
59
|
-
yield(key, value)
|
60
|
-
end
|
61
|
-
rescue Diplomat::KeyNotFound => exc
|
62
|
-
CMDB.log.warn exc.message
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
# Lazily parse a value, which may be valid JSON or may be a bare string.
|
68
|
-
# TODO: concat a regexp to match JSONable things
|
69
|
-
def process_value(val)
|
70
|
-
JSON.load(val)
|
71
|
-
rescue JSON::ParserError
|
72
|
-
val
|
73
|
-
end
|
74
|
-
|
75
|
-
# Converts the dotted notation to a slashed notation. If a @prefix is set, it applies the prefix.
|
76
|
-
# @example
|
77
|
-
# "common.proxy.endpoints" => common/proxy/endpoints (or) shard403/common/proxy/endpoints
|
78
|
-
def dot_to_slash(key)
|
79
|
-
key = "#{@prefix}.#{key}" unless @prefix.nil?
|
80
|
-
key.split('.').join('/')
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|