/labrador

Python-powered configuration retrieval

Primary LanguagePython

Labrador (Retriever)

A library for pulling strings from a variety of places:

  • Files!
  • Environment variables!
  • AWS Metadata!

This makes it easy to generate configuration data, with some amounts of smarts since it's just Python. We also include built-in output formatters to get your data out conveniently.

import labrador as l

config = {
    'hostname': l.get('file:///etc/hostname'),
    'iam_access_key': l.get('iam://my-iam-role/AccessKeyId') or "12345"
}

print l.dumps(config, fmt='yaml')

You can also use it straight off the command line as a Labrador-powered templating engine:

$ pip install labrador
$ echo 'hello ${file:///etc/hostname}' | lab

Built-in Retrievers

import labrador as l

l.get('file://test')               # relative file path
l.get('file:///etc/test')          # absolute file path /etc/test

l.get('env://FOO')                 # environment variable `FOO`

l.get('awsmeta://local-ipv4')      # retrieve AWS metadata
l.get('iam://my-role/AccessKeyId') # retrieve IAM credentials

l.get('facter://ipaddress')        # retrieve facter keys

Built-in Outputters

import labrador as l

l.dumps({'foo': 'bar'}, fmt='json')
l.dumps({'foo': 'bar'}, fmt='yaml')
l.dumps({'foo': 'bar'}, fmt='properties')
l.dumps({'mysection': {'foo': 'bar'}}, fmt='ini')

Extension via Dependency Injection

Both retrievers and outputters are dependency injected, i.e. you can get your own Labrador instance that inputs or outputs whatever you want with your own handler:

import labrador

def myhandler(uri):
    status = True
    value = "retrieved from: " + uri
    return (status, value)

l = labrador.Labrador(retrievers={'myprotocol': myhandler})
l.get('myprotocol://myresourceuri')
# => (True, "retrived from: myresoureuri")

In other words, a protocol handler is just a function that:

  1. Takes a URI
  2. Returns a 2-tuple of status information and the retrieved value.

Status information and the retrieved value can be whatever you want. The only required protocol is that None or False for status or value are always considered failure/lack of information present.

Caching

Labrador comes with built-in caching support. It is disabled by default but is enableable by dependency-injecting the cache object:

import labrador
import labrador.cache.yaml_cache

l = labrador.Labrador(
      cache=labrador.cache.yaml_cache.YamlCache('/home/me/.labcache.yml'))

l.get('https://github.com/torvalds.keys') # first time is slow; HTTPS request
l.get('https://github.com/torvalds.keys') # second time is fast; local disk access

The built-in local YAML cache is persistent by default; it's just a file stored on disk that can be re-read.

The caches are modular; they just have a get/put interface, so you can make any kind of cache (local, shared, expiring, excluding certain types of URIs) that you like:

class MyCache:
    def get(self, key):
        ...

    def put(self, key, value):
        ...

import labrador
l = labrador.Labrador(cache=MyCache())
l.get(...)