Managing Multiple Environments¶

Many applications and use-cases call for managing multiple environments and handling common settings between them. These environments might refer to infrastructure such as production, staging, or dev... or perhaps it might be to handle multiple accounts or regions within an account at a service provider.

The following example outlines one possible approach to managing multiple infrastructure accounts from an application Built on Cement:

myapp.py:

from cement.core.foundation import CementApp
from cement.core.controller import CementBaseController, expose
from cement.utils.misc import init_defaults

# set default settings for our different environments
defaults = init_defaults('myapp', 'env.production', 'env.staging', 'env.dev')
defaults['myapp']['default_env'] = 'production'
defaults['env.production']['foo'] = 'bar.production'
defaults['env.staging']['foo'] = 'bar.staging'
defaults['env.dev']['foo'] = 'bar.dev'

# do this in a hook so that we can load the default from config
def set_default_env(app):
    if app.pargs.env is None:
        app.pargs.env = app.config.get('myapp', 'default_env')

class MyController(CementBaseController):
    class Meta:
        label = 'base'
        arguments = [
            (['-E', '--environment'],
             dict(help='environment override',
                  action='store',
                  nargs='?',
                  choices=['production', 'staging', 'dev'],
                  dest='env')),
            ]

    @expose(hide=True)
    def default(self):
        print('Inside MyController.default()')

        # shorten things up a bit for clarity
        env_key = self.app.pargs.env
        env = self.app.config.get_section_dict('env.%s' % env_key)

        print('Current Environment: %s' % env_key)
        print('Foo => %s' % env['foo'])


class MyApp(CementApp):
    class Meta:
        label = 'myapp'
        config_defaults = defaults
        base_controller = MyController

with MyApp() as app:
    app.hook.register('post_argument_parsing', set_default_env)
    app.run()

myapp.conf

[myapp]
default_env = production

[env.production]
foo = bar.production

[env.staging]
foo = bar.staging

[env.dev]
foo = bar.dev

This looks like:

$ python myapp.py
Inside MyController.default()
Current Environment: production
Foo => bar.production

$ python myapp.py -E staging
Inside MyController.default()
Current Environment: staging
Foo => bar.staging

$ python myapp.py -E dev
Inside MyController.default()
Current Environment: dev
Foo => bar.dev

The idea being that you can maintain a single set of operations, but modify what or where those operations happen by simply toggling the configuration section (that has the same configuration settings per environment).