cmdb 2.6.2 → 3.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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