You are here

acquia_purge.diagnostics.inc in Acquia Purge 7

Same filename and directory in other branches
  1. 6 acquia_purge.diagnostics.inc

Diagnostic self-tests and reports that aim to prevent runtime misery.

File

acquia_purge.diagnostics.inc
View source
<?php

/**
 * @file
 * Diagnostic self-tests and reports that aim to prevent runtime misery.
 */

/**
 * Status.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_status($t, $service) {
  $passivemode = _acquia_purge_variable('acquia_purge_passivemode');
  $errorlimit = _acquia_purge_variable('acquia_purge_errorlimit');
  $stats = $service
    ->stats();
  $test = array(
    'title' => $t('Status'),
    'value' => $passivemode ? $t('Idle (passive mode)') : t('Idle'),
    'severity' => ACQUIA_PURGE_SEVLEVEL_INFO,
  );

  // When the user did not set a static integer as error limit, we calculate
  // it dynamically with limit = factor^3, with 20 as lowest limit. The higher
  // the factor is, the more tolerant our limit is. Limits for factors 1-14:
  // 20, 20, 27, 64, 125, 216, 343, 512, 729, 1000, 1331, 1728, 2197, 2744
  if (!is_int($errorlimit)) {
    $factor = $service
      ->capacity()
      ->httpRequestsFactor();
    $errorlimit = $factor <= 2 ? 20 : ceil(pow($factor, 3));
  }
  if ($stats['bad'] > $errorlimit) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('Acquia Purge failed over @limit times (@bad), since the last time the' . ' queue was processed. To avoid crashing your site with endless queue' . ' processing, cache invalidation has been disabled. We recommend you to' . " ask your site maintainer to check Drupal's watchdog logs for errors," . ' before lifting the blockade with "drush ap-forget".', array(
      '@limit' => $errorlimit,
      '@bad' => $stats['bad'],
    ));
    return $test;
  }

  // Report status if the system is running.
  if ($stats['running']) {
    if (!function_exists('theme')) {
      require_once DRUPAL_ROOT . '/includes/theme.inc';
    }
    $test['description'] = theme('acquia_purge_status_bar_widget', $stats);
    if ($stats['locked']) {
      $test['value'] = $test['value_plain'] = $t('Processing');
    }
    else {
      $test['value'] = $test['value_plain'] = $t('Items queued');
    }
  }
  return $test;
}

/**
 * Acquia Purge.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_acquia_purge($t, $service) {
  $test = array(
    'severity' => ACQUIA_PURGE_SEVLEVEL_INFO,
    'value' => ACQUIA_PURGE_VERSION,
    'title' => $t('Acquia Purge'),
  );

  // Detect the existence of the Boost module which is incompatible.
  if (module_exists('boost')) {
    $test['value'] = $t('Conflicts with: @m', array(
      '@m' => 'boost',
    ));
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('Your site has the boost module enabled which is' . ' known to cause issues on Acquia Cloud. Because of its heavy' . ' interactions with the file system it will destabilize your site.');
  }

  // Detect the existence of the Purge module which is incompatible for now.
  if (module_exists('purge')) {
    $test['value'] = $t('Conflicts with: @m', array(
      '@m' => 'purge (d7)',
    ));
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('Your site has the Purge module enabled which' . ' is incompatible with Acquia Purge for Drupal 7. However, the version' . ' for Drupal 8 depends on it, but please remove it on this setup!');
  }

  // Detect if the Varnish module is enabled, which isn't necessary.
  if (module_exists('varnish')) {
    $test['value'] = $t('Conflicts with: @m', array(
      '@m' => 'varnish',
    ));
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Your site runs with the varnish module enabled,' . ' which is known to not work on Acquia Cloud. As Acquia Purge does its' . ' work already for you we strongly encourage you to remove it.');
  }

  // Detect if the Domain Access module is enabled, which is recipe for trouble.
  if (module_exists('domain')) {
    $test['value'] = $t('Conflicts with: @m', array(
      '@m' => 'Domain Access',
    ));
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Domain Access allows operating your site on' . ' multiple domain names and slicing content among those domains. This' . ' conflicts with Acquia Purge as editors could update a piece of' . ' content on one domain, which would then get cleared on all domains' . ' that Acquia Purge operates on. On top of this, Acquia Purge operates' . ' a queue which means that DA-domain information gets lost after saving' . ' content. Therefore, unless you are not splitting content with DA, you' . ' are recommended to reconsider your architecture.');
  }

  // Detect if the shield module is in use.
  if (module_exists('shield')) {
    $test['value'] = $t('Conflicts with: @m', array(
      '@m' => 'Shield',
    ));
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('The Shield module is in use on your site and' . ' protects your content with a password. Because Varnish caches these' . ' items differently, the instructions Acquia Purge sends to Varnish' . " will not work. This inconvenience requires you to either disable" . ' Acquia Purge or enable the passivemode setting. Please see' . ' INSTALL.md and https://www.drupal.org/node/2642458 for more info.');
  }
  return $test;
}

/**
 * Acquia environment.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_acquia_environment($t, $service) {
  $test = array(
    'title' => $t('Acquia environment'),
  );
  if ($service
    ->hostingInfo()
    ->isThisAcquiaCloud()) {
    $test['value'] = $t('Acquia Cloud (@name.@env)', array(
      '@name' => $service
        ->hostingInfo()
        ->getSiteName(),
      '@env' => $service
        ->hostingInfo()
        ->getSiteEnvironment(),
    ));
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_INFO;
  }
  else {
    $test['value'] = $t('Third-party');
    $test['description'] = $t('Acquia Purge only works on Acquia Cloud and has' . ' disabled itself as this environment appears different. Any items in' . ' the queue will get processed as soon as you deployed your site.');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
  }
  return $test;
}

/**
 * Acquia auth token.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_acquia_auth_token($t, $service) {
  $test = array(
    'title' => $t('Acquia auth token'),
    'value' => $service
      ->hostingInfo()
      ->getBalancerToken(),
    'severity' => ACQUIA_PURGE_SEVLEVEL_INFO,
  );
  if ($service
    ->oddities()
    ->has('405')) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Acquia Purge received HTTP 405 (not allowed) responses from one of' . ' your load balancers! What this suggests is that your authentication' . ' token is (or has been) invalid. If cache invalidation constantly' . ' appears to fail, please contact Acquia support.');
  }
  elseif (!strlen($service
    ->hostingInfo()
    ->getBalancerToken())) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('Empty token configured!');
  }
  return $test;
}

/**
 * Loaded state storage.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_backends_statestorage($t, $service) {
  $test = array(
    'title' => $t('Loaded state storage'),
  );
  if ($service
    ->hostingInfo()
    ->isMemcachedUsed()) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_INFO;
    $test['value'] = 'Memcached';
  }
  else {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['value'] = $t('File: @p', array(
      '@p' => ACQUIA_PURGE_STATE_FILE,
    ));
    $test['description'] = $t('It is highly recommended to setup Memcached for' . ' your website. Cache invalidation will benefit from it, but the' . ' overall benefit to your site is far larger. https://docs.acquia.com' . '/acquia-cloud/performance/memcached');
  }
  return $test;
}

/**
 * Loaded processors.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_backends_processor($t, $service) {
  $test = array(
    'title' => $t('Loaded processors'),
    'value' => array(
      $t('Ajax'),
    ),
    'severity' => ACQUIA_PURGE_SEVLEVEL_INFO,
  );

  // Add tests for lateruntime and cron processing modes, when enabled.
  if (_acquia_purge_variable('acquia_purge_lateruntime')) {
    $test['value'][] = $t('Runtime');
  }
  if (_acquia_purge_variable('acquia_purge_cron')) {
    $test['value'][] = $t('Cron');

    // Raise a warning when it was too long ago since cron ran.
    $cron_last = variable_get('cron_last');
    if (!is_numeric($cron_last)) {
      $cron_last = variable_get('install_time', 0);
    }
    if (REQUEST_TIME - $cron_last > 3600) {
      $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
      $test['description'] = $t('It has been longer than one hour since cron ran' . ' for the last time, which puts your site at risk. As Acquia Purge' . ' has cron processing enabled, it assumes processing of its queue.' . ' When this is not frequent enough, the queue can reach dangerous' . ' levels and eventually even lead to a safety shutdown.');
    }
  }
  $test['value'] = implode(', ', $test['value']);
  return $test;
}

/**
 * Loaded executors.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_backends_executor($t, $service) {
  $executors = $service
    ->executorIds();
  foreach ($executors as $n => $id) {
    $executors[$n] = str_replace('AcquiaPurgeExecutor', '', $id);
  }
  $test = array(
    'value' => implode(', ', $executors),
    'title' => $t('Loaded executors'),
    'severity' => ACQUIA_PURGE_SEVLEVEL_INFO,
  );
  if (empty($executors)) {
    $test['value'] = $t('None');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t("Can't work without loaded executors!");
  }
  return $test;
}

/**
 * Loaded queue.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_backends_queue($t, $service) {
  $test = array(
    'title' => $t('Loaded queue'),
    'value' => t('Database'),
    'severity' => ACQUIA_PURGE_SEVLEVEL_INFO,
  );
  if (_acquia_purge_variable('acquia_purge_smartqueue')) {
    $test['value'] = t('Smart database (drops old items)');
  }
  return $test;
}

/**
 * Invalidated schemes.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_inv_schemes($t, $service) {
  $schemes = $service
    ->hostingInfo()
    ->getSchemes();
  $test = array(
    'title' => $t('Invalidated schemes'),
    'value' => strtoupper(implode(', ', $schemes)),
  );

  // Test if no protocol schemes are to be invalidated.
  if (empty($schemes)) {
    $test['value'] = $t('None');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('It seems as if you have put both ' . ' <b>acquia_purge_http</b> and <b>acquia_purge_https</b> settings to' . ' <b>FALSE</b> and thus disabled, which leads to a situation' . ' that does not work. Enable either one of them for Acquia Purge' . ' to continue to operate.');
  }
  elseif (count($schemes) === 1) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_OK;
  }
  elseif (count($schemes) === 2 && count($service
    ->hostingInfo()
    ->getDomains()) > 4) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('You are clearing both <b>http://</b> as' . ' <b>https://</b> paths on your site with more than 4 domain names.' . ' Acquia Purge has self-shutdown to prevent worse from happening as' . ' two protocol schemes with this many domain names can likely bring' . ' down your website at some point. Its better to either only wipe' . ' https:// or by reducing unnecessary domain names, for instance' . ' bare domains that redirect. See INSTALL.md and DOMAINS.md for more' . ' information on tuning.');
  }
  elseif (count($schemes) === 2) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('You are clearing both <b>http://</b> as' . ' <b>https://</b> paths on your site, which can be dangerous. Especially' . ' if you have more than 1-2 domains as this will lead in the double' . ' amount of work that gets done. Keep a close eye on the queue and' . ' consider delivering all your traffic off https:// (by doing a full' . ' -site redirect) and to disable http:// clearing, see INSTALL.md.');
  }
  return $test;
}

/**
 * Invalidated domains.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_inv_domains($t, $service) {
  $allriskmode = _acquia_purge_variable('acquia_purge_allriskmode');
  $hosting_info = $service
    ->hostingInfo();
  $domains_link = drupal_get_path('module', 'acquia_purge') . '/DOMAINS.md';
  $domains_link = url($domains_link);
  $domains = $hosting_info
    ->getDomains();
  $domains_c = count($domains);
  $test = array(
    'value' => implode(', ', $domains),
    'value' => $t('@mode: @domains', array(
      '@domains' => implode(' ', $domains),
      '@mode' => $hosting_info
        ->areDomainsHardcoded() ? $t('Manual') : $t('Automatic'),
    )),
    'title' => $t('Invalidated domains'),
    'description' => $t('The domains for which content gets cleared from your' . ' load balancers. Every domain name multiplies the work to be done,' . ' it is therefore important to <a href="!link" target="_blank">' . 'specify your domains</a> when the automatically detected list exceeds' . ' 4 domains or when it is incorrect.', array(
      '!link' => $domains_link,
    )),
  );

  // One cannot have wildcards in domain names, test for this.
  $wildcard_domains_found = FALSE;
  foreach ($domains as $domain) {
    if (strpos($domain, '*') !== FALSE) {
      $wildcard_domains_found = $domain;
      break;
    }
  }

  // Evaluate domain names configuration and kick off warnings or even a full
  // block when the situation asks for it.
  if (!$domains_c) {
    $test['value'] = $t('0 domains detected.');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('No domains have been detected which implies that' . ' something is seriously wrong. Pending purges will not be processed.');
  }
  elseif ($wildcard_domains_found) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('The domain "@baddomain" has a asterisk in it,' . ' which implies that its subdomains require clearing. However, this is' . ' unfortunately not supported by Acquia Cloud and therefore will lead' . ' to purge failure. Please remove this domain from your configuration.', array(
      '@baddomain' => $wildcard_domains_found,
    ));
  }
  elseif (php_sapi_name() === 'cli' && in_array('default', $domains)) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('The domain name "default" has been found' . ' indicating that you are running via Drush. Either you will need to' . ' specify your domains or provide --uri for the right site.');
  }
  elseif ($domains_c <= 6) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_OK;
    unset($test['description']);
  }
  elseif ($domains_c > 6 && $domains_c <= 8) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Because you have @no domain names there is a' . ' <b>high risk</b> that clearing your site will put stress on your' . ' servers, it is <b>strongly recommended</b> to <a href="!link"' . ' target="_blank">specify your domains</a> to not exceed 6 domains.', array(
      '!link' => $domains_link,
      '@no' => $domains_c,
    ));
  }
  elseif ($allriskmode) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Because you have @no domain names there is a' . ' <b>high risk</b> that clearing your site will put stress on your' . ' servers, it is <b>strongly recommended</b> to <a href="!link"' . ' target="_blank">specify your domains</a> to not exceed 6 domains.', array(
      '!link' => $domains_link,
      '@no' => $domains_c,
    ));
  }
  else {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('Because you have @no domain names there is a' . ' <b>very high risk</b> that clearing your site will put stress on your' . ' servers, it is <b>strongly recommended</b> to <a href="!link"' . ' target="_blank">specify your domains</a> to not exceed 6 domains. To' . ' prevent system failure, pending purges will not be processed!', array(
      '!link' => $domains_link,
      '@no' => $domains_c,
    ));
  }
  return $test;
}

/**
 * Invalidated balancers.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_inv_balancers($t, $service) {
  $test = array(
    'value' => implode(', ', $service
      ->hostingInfo()
      ->getBalancerAddresses()),
    'title' => $t('Invalidated balancers'),
  );

  // When HTTP_X_GEO_COUNTRY is detected, register it as VCL oddity.
  if (isset($_SERVER['HTTP_X_GEO_COUNTRY'])) {
    if (strlen($_SERVER['HTTP_X_GEO_COUNTRY'])) {
      $service
        ->oddities()
        ->add('geoip');
    }
  }

  // Evaluate the sanity of the amount of load balancers.
  $balancers_c = count($service
    ->hostingInfo()
    ->getBalancerAddresses());
  if (!$balancers_c) {
    $test['value'] = $t('No load balancers detected.');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('There are no load balancers detected which makes' . ' it impossible to purge your site. Please contact Acquia Support!');
  }
  elseif ($balancers_c < 2) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('You have only one load balancer, this means your' . ' site can not be failed over by us. Please contact Acquia Support.');
  }
  elseif ($balancers_c >= 6) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Your website is running behind @no load' . ' balancers, which will put severe stress on your database. Please pay' . ' attention to the number of items in the queue table.', array(
      '@no' => $balancers_c,
    ));
  }
  elseif ($service
    ->oddities()
    ->has('geoip')) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('The HTTP_X_GEO_COUNTRY variable has been detected, which indicates' . ' that you have GeoIP enabled. Acquia Purge automatically switched to' . ' issuing BAN-requests. However, this only works behind a Varnish 4' . ' load balancer. When you are seeing HTTP 501 errors in your logs, then' . ' you require upgraded load balancers. Acquia is in the process to' . ' upgrade its entire platform.');
  }
  elseif ($service
    ->oddities()
    ->has('403')) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Acquia Purge received HTTP 403 (forbidden) responses from one of your' . ' load balancers, this is abnormal behavior! This could suggest that' . ' your site runs behind a customized VCL file which likely hinders the' . ' proper functioning of this module. Please contact Acquia support with' . ' this message to get further guidance on how to resolve the problem.');
  }
  else {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_OK;
  }
  return $test;
}

/**
 * Expire module.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_expire_module($t, $service) {
  $test = array(
    'title' => $t('Expire module'),
  );

  // If expire is not installed, Acquia Purge can still work as API.
  if (!module_exists('expire')) {
    $test['value'] = $t('Not installed!');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('The expire module is not present, which only' . ' makes sense if you want to use Acquia Purge as pure API (and have' . ' removed expire from acquia_purge.info). If you want your content to' . ' be cleared when is changing, enable it back on again.');
    return $test;
  }

  // Read out Expire's 'expire_status' variable.
  $statuses = array(
    EXPIRE_STATUS_ENABLED_EXTERNAL => $t('External expiration'),
    EXPIRE_STATUS_ENABLED_INTERNAL => $t('Internal expiration'),
    EXPIRE_STATUS_DISABLED => $t('Disabled'),
    'n/a' => $t('unknown'),
  );
  $status = (int) variable_get('expire_status', EXPIRE_STATUS_DISABLED);
  $status_t = isset($statuses[$status]) ? $statuses[$status] : $statuses['n/a'];

  // Test Expire's 'expire_include_base_url' variable, which should be FALSE.
  if (variable_get('expire_include_base_url', EXPIRE_INCLUDE_BASE_URL)) {
    $test['value'] = $t('Include base URL in expires');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t("This setting must be disabled as Acquia Purge" . ' cannot process full URLs but operates on paths instead.');
  }
  elseif (variable_get('expire_debug', EXPIRE_DEBUG_DISABLED)) {
    $test['value'] = $t('Debugging enabled');
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t("Expire's debugging is enabled which is highly" . ' discouraged! Acquia Purge also logs very heavily and together with' . ' Expire debugging, this could break down your site!');
  }
  elseif ($status === EXPIRE_STATUS_ENABLED_EXTERNAL) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_OK;
    $test['value'] = $t('Delegates URLs to Acquia Purge');
  }
  else {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['value'] = $t('Status: @status', array(
      '@status' => $status_t,
    ));
    $test['description'] = $t('The Expire module operates in the "@status" mode' . ', this means that Expire will not delegate URLs to Acquia Purge when' . ' you are updating content. Change it to "External expiration".', array(
      '@status' => $status_t,
    ));
  }
  return $test;
}

/**
 * Page cache.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_page_cache($t, $service) {
  $cache = variable_get('cache', 0);
  $cache_backend = $service
    ->hostingInfo()
    ->getPageCacheBackend();
  $test = array(
    'severity' => ACQUIA_PURGE_SEVLEVEL_OK,
    'value' => $cache ? $t('Enabled (@b)', array(
      '@b' => $cache_backend,
    )) : $t('Disabled'),
    'title' => $t('Page cache'),
  );

  // See https://www.drupal.org/node/2330805
  if (!$cache && module_exists('authcache')) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_OK;
    $test['value'] = $t('Disabled (because of Authcache)');
  }
  elseif (!$cache) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('Your site has Drupal page caching disabled,' . ' which is of extreme importance to every website that gets traffic.' . ' Once enabled your load balancer will be instructed to start serving' . ' pages from its cache to offload traffic from your web servers. The' . ' more traffic served from your load balancer, the faster your' . ' site will be!');
  }
  elseif ($service
    ->hostingInfo()
    ->isPageCacheFake()) {
    $test['value'] = $t("Enabled (DrupalFakeCache, executor disabled)");
  }
  elseif ($service
    ->oddities()
    ->has('wildcards')) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Your site invalidated one or more paths with' . ' wildcard characters in them in the past, e.g. "news/*". Because of' . ' this, you are now recommended to configure DrupalFakeCache as' . " documented on https://www.drupal.org/node/797346. If you don't do" . ' this, outdated cached copies will be returned from the page cache' . ' for paths you intended to clear with a wildcard.');
  }
  return $test;
}

/**
 * Page caching time (TTL).
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_page_caching_time_ttl($t, $service) {
  $age = (int) variable_get('page_cache_maximum_age', 0);
  $test = array(
    'title' => $t('Page caching time (TTL)'),
  );

  // Set the value label.
  $test['value'] = $t('Disabled');
  if ($age !== 0) {
    $test['value'] = array(
      '@age' => round($age / 60 / 60, 2),
    );
    $test['value'] = $t('@age hours', $test['value']);
  }

  // Determine what "find out more" link we can provide.
  $link = 'http://blog.merge.nl/20120118/how-does-caching-work-drupal';
  if (function_exists('user_access')) {
    if (function_exists('user_access') && user_access('administer site configuration')) {
      $link = url('admin/config/development/performance');
    }
  }

  // Users without a TTL configured won't put our module to much use.
  if ($age === 0) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('The "<a href="!link" target="_blank">expiration' . ' of cached pages</a>"-setting of your website is disabled, while we' . ' strongly recommend to put it to 6 hours or longer in combination with' . ' the Acquia Purge module. The setting determines how long external' . ' caches (like our load balancers) are instructed to keep a cached copy' . ' of content. With your current setting your load balancer is not being' . ' used and your web servers will likely be under constant stress,' . ' causing a slow site and  system resources being wasted.', array(
      '!link' => $link,
    ));
  }
  elseif ($age < 21600) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('The "<a href="!link" target="_blank">expiration' . ' of cached pages</a>"-setting of your website is set to @age, while we' . ' strongly recommend to put it to 6 hours or longer in combination with' . ' the Acquia Purge module. The setting determines how long external' . ' caches (like our load balancers) are instructed to keep a cached copy' . ' of content. With your current configuration, your load balancer is not' . ' used effectively and your web servers will likely be under stress,' . ' causing a slow site and system resources being wasted.', array(
      '!link' => $link,
      '@age' => $test['value'],
    ));
  }
  else {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_OK;
  }
  return $test;
}

/**
 * Capacity.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_capacity($t, $service) {
  $capacity = $service
    ->capacity();
  $maxitems = $capacity
    ->queueClaimsLimit();
  $factor = $capacity
    ->httpRequestsFactor();
  $test = array(
    'title' => $t('Capacity'),
    'severity' => ACQUIA_PURGE_SEVLEVEL_OK,
    'value' => $t('@items items/batch, slowdown factor=@factorX', array(
      '@items' => $maxitems,
      '@factor' => $factor,
    )),
  );
  return $test;
}

/**
 * Logging.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_logging($t, $service) {
  $test = array(
    'severity' => ACQUIA_PURGE_SEVLEVEL_OK,
    'value' => $t('Successes and failures are logged'),
    'title' => $t('Logging'),
  );
  if (_acquia_purge_variable('acquia_purge_log_success') !== TRUE) {
    $test['value'] = $t('Only failures are logged');
  }
  return $test;
}

/**
 * Queue safety.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_queue_safety($t, $service) {
  $qitems = $service
    ->stats()['remaining'];

  // Define the warning and shutdown thresholds and setup $test.
  $factor = $service
    ->capacity()
    ->httpRequestsFactor();
  $warning = ceil(3000 / $factor);
  $shutdown = ceil(10000 / $factor);
  $tvars = array(
    '@qitems' => $qitems,
    '@warning' => $warning,
    '@shutdown' => $shutdown,
  );
  $test = array(
    'title' => $t('Queue safety'),
    'value' => $t('Warning at @warning, shutdown at @shutdown items', $tvars),
  );

  // Test the queue level for these thresholds and act appropriately.
  if ($qitems < $warning) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_OK;
  }
  elseif ($qitems >= $warning && $qitems < $shutdown) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Your queue holds @qitems items now which is' . ' regarded as dangerous, as it either means that your are clearing too' . ' broadly (domains, http:// and https://) or that your installation is' . ' not able to keep up processing. If you are not using cron for' . ' processing, consider this now and if you already are, consider a' . ' shorter cron interval. If you have many domain names, consider' . ' reducing the domains. As safety measure, Acquia Purge will shut' . ' itself down once the queue reaches @shutdown items!', $tvars);
  }
  elseif (_acquia_purge_variable('acquia_purge_allriskmode') && $qitems >= $shutdown) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_WARNING;
    $test['description'] = $t('Your queue contains @qitems items and crossed' . ' the safety limit of @shutdown items, Acquia Purge has not shut itself' . ' down because you enabled "all risk mode". Because of this, you are' . ' not entitled to Acquia support for any tickets related to queue' . ' processing. You are being highly encouraged to lower your slowdown' . ' factor by reducing domain names and/or increasing your cron interval' . ' or enable cron processing when you have not yet done this.', $tvars);
  }
  elseif ($qitems >= $shutdown) {
    $test['severity'] = ACQUIA_PURGE_SEVLEVEL_ERROR;
    $test['description'] = $t('Your queue contains @qitems items and crossed' . ' the safety limit of @shutdown items, Acquia Purge has shut itself' . ' down to prevent your system from thrashing. What this means is that' . ' you are either clearing too broadly (domains, http:// and https://)' . ' or that your installation is not able to keep up. If you are not' . ' using cron for processing, set this up now and if you already are,' . ' increase your cron interval! If you have many domain names, reduce' . ' the list. Clear Varnish manually and run drush ap-forget to clear' . ' your queue and unblock the safety shutdown.', $tvars);
  }
  return $test;
}

/**
 * Queue.
 *
 * @param string $t
 *   Name of the t() function to call.
 * @param AcquiaPurgeService $service
 *   The Acquia Purge service.
 *
 * @return array
 *   Associative array with the following elements:
 *   - title: The name of the requirement.
 *   - value: The current value (e.g., version, time, level, etc).
 *   - description: The description of the requirement/status.
 *   - severity:
 *       - ACQUIA_PURGE_SEVLEVEL_INFO
 *       - ACQUIA_PURGE_SEVLEVEL_OK
 *       - ACQUIA_PURGE_SEVLEVEL_WARNING
 *       - ACQUIA_PURGE_SEVLEVEL_ERROR <-- blocks Acquia Purge from executing!
 */
function _acquia_purge_diagnostics_queue($t, $service) {
  $stats = $service
    ->stats();
  if ($stats['remaining'] === 0) {
    $value = $t('Empty');
  }
  else {
    $value = $t('Contains @d items.', array(
      '@d' => $stats['remaining'],
    ));
  }
  return array(
    'title' => $t('Queue'),
    'value' => $value,
    'severity' => ACQUIA_PURGE_SEVLEVEL_OK,
  );
}