Environment aware Drupal sites

Posted by on

As a course of developing for larger Drupal sites, you typically find yourself having multiple environments, one for development, one or more for staging or user acceptance testing, and another for production (and perhaps disaster recovery).

One thing that always comes up is making Drupal “environment” aware, so it knows how it should behave, what modules should be turned on (or off) and what servers it should be talking to for instance.

Environment module

The environment module allows you to define arbitrary environments (it also comes with some out of the box) in code. Example hook

 1 <?php
 2 /**
 3  * Implements hook_environment().
 4  */
 5 function HOOK_config_environment() {
 6   return array(
 7     'prod' => array(
 8       'label' => t('Production'),
 9       'description' => t('Live sites are in full production and browsable on the web.'),
10       'allowed' => array(
11         'default' => TRUE,
12       ),
13     ),
14     'dev' => array(
15       'label' => t('Development'),
16       'description' => t('Developer machines.'),
17       'allowed' => array(
18         'default' => FALSE,
19       ),
20     ),
21     'staging' => array(
22       'label' => t('Staging'),
23       'description' => t('Bug fixes and testing are done here.'),
24       'allowed' => array(
25         'default' => FALSE,
26       ),
27     ),
28   );
29 }
30 ?>

You also may want to remove the default environments that come with the environment module

 1 <?php
 2 /**
 3  * Implements hook_environment_alter().
 4  */
 5 function HOOK_config_environment_alter(&$environments) {
 6   // Remove default environments.
 7   unset($environments['production']);
 8   unset($environments['development']);
 9 }
10 ?>

Using this code, and some magic in settings.php, you can effectively tell Drupal which environment it is by switching on the HTTP_HOST. A stripped back example is:

 1 <?php
 2 // Include environment-specific config by parsing the URL.
 3 // To override this, set $environment in settings.php
 4 // BEFORE including this file.
 5 if (!isset($environment)) {
 6   if (strpos($_SERVER['SERVER_NAME'], '.demo.net.nz') !== FALSE) {
 7     $environment = 'staging';
 8   }
 9   elseif (strpos($_SERVER['SERVER_NAME'], 'local') !== FALSE) {
10     $environment = 'dev';
11   }
12   else {
13     // Default to production.
14     $environment = 'prod';
15   }
16 }
17 // The environment module uses a lowercase variable.
18 $conf['environment']['default'] = $environment;
19 define('ENVIRONMENT', $environment);
20 
21 // Load the environment config file, followed by host-specific
22 // over-rides (if any) in non-production environments.
23 $conf_path = DRUPAL_ROOT . "/sites/default/";
24 require $conf_path . "settings.$environment.php";
25 ?>

Note this also lets you include a separate PHP file called “settings.dev.php” for development, where variable overrides (using global $conf) can be done on a per-environment basis.

Environment switching

A natural extension of the environment module is to allow pulling a production database back to development, and then “switching” it into a development state. This switching is already a hook you can implement, allowing you to react on both the current and target environments.

An example of this is from the module’s homepage:

 1 <?php
 2 /**
 3  * Implementation of hook_environment_switch().
 4  */
 5 function HOOK_environment_switch($target_env, $current_env) {
 6   // Declare each optional development-related module
 7   $devel_modules = array(
 8     'context_ui',
 9     'devel',
10     'devel_generate',
11     'devel_node_access',
12     'update',
13     'views_ui',
14   );
15   switch ($target_env) {
16     case 'production':
17       module_disable($devel_modules);
18       drupal_set_message('Disabled development modules');
19       return;
20     case 'development':
21       module_enable($devel_modules);
22       drupal_set_message('Enabled development modules');
23       return;
24   }
25 }
26 ?>

Other common things we do in our environment switch to development include:

Removing JS and CSS aggregation

1 <?php
2 variable_set('preprocess_css', 0);
3 variable_set('preprocess_js', 0);
4 drupal_set_message(t('Removed aggregation from CSS and JS.'));
5 ?>

Changing the Apache Solr environment to point at localhost

 1 <?php
 2 if (module_exists('apachesolr')) {
 3   $solr_env = array(
 4     'url' => 'http://127.0.0.1:8983/solr/dev',
 5     'make_default' => TRUE,
 6     'name' => 'DEV',
 7     'env_id' => 'solr',
 8     'service_class' => '',
 9     'conf' => array(
10       'apachesolr_read_only' => '0',
11     ),
12   );
13   apachesolr_environment_save($solr_env);
14   drupal_set_message(t('Set solr environment to @name at @url', array(
15     '@name' => $solr_env['name'],
16     '@url' => $solr_env['url'],
17   )));
18 }
19 ?>

Preventing API writes to production systems

1 <?php
2 variable_set('brightcove_api_write_enabled', 0);
3 drupal_set_message(t('Stopped the Brightcove write API sync.'));
4 ?>

Granting helpful debugging permissions to certain roles

 1 <?php
 2 if (module_exists('devel')) {
 3   $dev_perms = array(
 4     'access devel information',
 5     'switch users',
 6   );
 7   user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, $dev_perms);
 8   user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, $dev_perms);
 9 }
10 ?>

The list goes on here.

Environment indicator module

Now that Drupal is environment aware, it is really helpful if Drupal can inform the user what environment they are currently looking at. Out of the box the environment module has no UI, so enter the module environment indicator to come save the day.

The new 7.x-2.x branch of environment indicator contains a lot of improvements over the 7.x-1.x branch, one of which is the integration with the core toolbar and shortcut modules.

To illustrate this, here is a screenshot of our toolbar in development (some links were stripped)

Environment indicator and the toolbar module working together

The module also alters the favicon to include a tiny letter and coloured background to match the colour you chose

Environment indicator and the favicon working together

This way you will never again forget which environment you are using, as the colours will be right there at the top of every page.

Comments

I am keen to hear how other people solve the issue of environment aware Drupal applications, and how this is communicated to the end users of the application. What other modules are out there? What experiences have you had?