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.
@@ -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
@@ -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
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module CMDB
3
- VERSION = '2.6.2'.freeze
3
+ VERSION = '3.0.0rc1'.freeze
4
4
  end
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
- # Values of RACK_ENV/RAILS_ENV that are considered to be "development," which relaxes
7
- # certain runtime sanity checks.
8
- DEVELOPMENT_ENVIRONMENTS = [nil, 'development', 'test'].freeze
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 asked for an invalid or malformed key name.
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 namespaces have an identical name. This isn't an error
87
- # when CMDB is used to refer to keys by their full, qualified name, but it can become
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/consul_source'
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: 2.6.2
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-03-15 00:00:00.000000000 Z
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.0'
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: '0'
103
+ version: 1.3.1
122
104
  requirements: []
123
105
  rubyforge_project:
124
106
  rubygems_version: 2.4.5.1
data/TODO.md DELETED
@@ -1,3 +0,0 @@
1
- Actually respect --quiet
2
-
3
- Remove reset/late_initialize garbage.
@@ -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