Command-line tool for configuration management databases
CMDB is a Ruby interface for consuming data from one or more configuration management databases
(CMDBs) and making that information available to Web applications.
It is intended to support multiple CM technologies, including:
Maintained by
With CMDB, you can:
CMDB has three primary interfaces:
cmdb shell
command navigates your k/v store using filesystem-likels
, cd
, and so forth) with color output and tab completioncmdb shim
command populates the environment with values and/or rewrites hardcodedCMDB::Interface
object provides a programmatic API for querying CMDBs. Its #to_env
Sources are specified with the --source
option when you run the CLI. This
option applies to all subcommands (shim
, shell
, etc) and must appear
before the subcommand name.
You can add as many sources as you’d like. All sources are specified as a URI,
where the scheme tells CMDB which driver to use and how to interpret the rest
of the URI.
Examples:
file:///var/lib/cmdb/myapp.yml
creates a file source with the prefixmyapp
; the value foo: bar
in the file would have the key myapp.foo
consul://localhost
creates a source with no key prefix that talks to a localfoo/bar
in Consul wouldconsul://kv:18500/myapp
creates a source with the prefix myapp.
that/myapp/
and their keymyapp.
consul://localhost/mycorp/staging/myapp
creates a source with the prefixmyapp.
; this source only “sees” Consul values under the pathstaging/myapp
and their key names always begin with myapp.
consul://localhost/mycorp/staging
creates a source with the prefix staging.
myapp
source in the example above!)If no sources are specified on the command line, CMDB will run an auto-detect
algorithm to check for network agents listening at localhost.
To learn more about sources and prefixes, see “Data model,” below.
To enter an interactive sh-like shell, just type cmdb shell
.
For non-Ruby applications, or for situations where CMDB values are required
outside of the context of interpreted code, use cmdb shim
to run
your application. The shim can do several things for you:
ENV
, or by rewriting files)www-data
)If you have an app that uses 12-factor (dotenv) style configuration, the shim
can populate the environment with CMDB values:
bundle exec cmdb shim
# Now your app can refer to ENV['DB_HOSTNAME'] or ENV['WIDGETS_FLAVORS]
# Note missing "my_app" prefix that would be present if you asked for these using their CMDB key names
Note that when we export CMDB keys into the environment, we remove the prefix of
each key; in the example above, the values could have come from common.db.hostname
and myapp.widgets.flavors
but their names have been simplified. If any two sources
have keys whose simplified names are identical, CMDB prints a detailed error message
and fails rather than putting ambiguous data into the environment.
Note that the data type of CMDB inputs is preserved: lists remain lists, numbers remain numbers,
and so forth. This works irrespective of the format of your configuration files, and also holds true
for CMDB values that are serialized to the environment (as a JSON document, in the case of lists).
If the --rewrite
option is provided, the shim recursively scans the provided
subdirectory for data files that contain replacement tokens; when a token is
found, it substitutes the corresponding CMDB key’s value.
Replacement tokens look like this: <<name.of.my.key>>
and can appear anywhere in a file as a YAML
or JSON value (but never a key). Unlike environment variables, replacement tokens always use
the fully-qualified key name, including prefix.
Replacement tokens should appear inside string literals in your configuration files so they don’t
invalidate syntax or render the files unparsable by other tools.
The shim performs replacement in-memory and saves all of the edits at once, making the rewrite
operation nearly atomic. If any keys are missing, then no files are changed on disk and the shim
exits with a helpful error message.
Given my_app.yml
and an application with two configuration files:
# config/database.yml
production:
host: <<my_app.db.hostname>
database: my_app_production
# config/widgets.json
{'widgetFlavors': '<<my_app.widgets.flavors>>'}
I can run the following command in my application’s root directory:
bundle exec cmdb shim --dir=config rackup
This will rewrite the files under config, replacing my configuration files as
follows:
# config/database.yml
production:
host: db1.local
database: my_app_production
# config/widgets.json
{'widgetFlavors':['vanilla', 'chocolate']}
If your app doesn’t know how to safely switch to a non-privileged user, the shim
can do this for you. Just add the --user
flag when you invoke it:
bundle exec cmdb shim --user=www-data whoami
To read CMDB data from a consul server, add a CLI parameter such as
--source=consul://some-host/key/subkey
. This will create a source whose
prefix is subkey
that encompasses the subtree of the k/v store that lies
underneath /key/subkey
.
To read CMDB data from a flat file on disk, add a CLI parameter such as
--source=file:///var/lib/cmdb/mykeys.yml
. This will parse the YAML file
located in /var/lib/cmdb
and present it as a source whose prefix is mykeys
.
JSON and YAML files are both supported. The structured data within each file
can contain arbitrarily-deep subtrees which are interpreted as subkeys,
sub-subkeys and so forth.
CMDB models all data sources as trees whose nodes are named, and whose leaf
nodes can contain a piece of data: strings, numbers, booleans, or arrays.
Maps are disallowed on order to prevent ambiguity; a map always represents a
subtree of the k/v store, never a value. Mixed-type arrays are disallowed
because they can cause problems with strongly-typed languages. Nil is
disallowed as a value because writing nil means “delete the key.”
Paths within a tree – and therefore CMDB keys – are identified using a dot
notation similar to Java properties; for instance, production.http.listen_port
might be an Integer-valued key that tells your microservice which HTTP port
to listen on in the production environment; production.database.host
might be
the database host, and so forth. The names of keys are determined by the tree
structure of your k/v store, and when you set a key through CMDB, its position
in the tree is derived from its key name.
CMDB sources have a prefix
, meaning that all keys contained in
that source begin with the same prefix. No two sources may share a prefix,
ensuring that sources don’t “hide” each others’ data. The prefix of a source is
usually automatically determined by the final component of its URL, e.g. the
filename in the case of file://
sources and the final path component in the
case of consul://
or other network sources.
The uniqueness constraint on prefixes means that all sources’ keys are
disjoint; there is no such thing as “inheritance” in the CMDB data model.
When keys are exported to the environment, the prefix is stripped from the
key name; however, CMDB still prevents overlap in this case.
Inheritance may be supported in future as an optional behavior, but is omitted
for the time being because in practice, it causes more problems than it solves.
Consider a file that defines the following variables:
# confusing.yml
this:
is:
ambiguous
was:
very: ambiguous
extremely: confusing
At first glance, ths file defines two CMDB keys:
confusing.this.is
(a string)confusing.this.was
(a map)However, an equally valid interpretation would be:
confusing.this.is
confusing.this.was.very
confusing.this.was.extremely
Because CMDB keys cannot contain maps, the first interpretation is wrong. The second
interpretation is valid according to the data model, but results in a situation where the type
of the keys could change if the structure of the YML file changes.
For this reason, any YAML file that defines an “ambiguous” key name will cause an error at
initialization time. To avoid ambiguous key names, think of your YAML file as a tree and remember
that leaf nodes must define data and internal nodes must define structure.