diff --git a/lib/Drupal/monitoring/Sensor/GitRepositorySensorException.php b/lib/Drupal/monitoring/Sensor/GitRepositorySensorException.php index 29b2d13..4a72a65 100644 --- a/lib/Drupal/monitoring/Sensor/GitRepositorySensorException.php +++ b/lib/Drupal/monitoring/Sensor/GitRepositorySensorException.php @@ -2,13 +2,13 @@ /** * @file - * Contains \Drupal\monitoring\Sensor\DisabledSensorException. + * Contains \Drupal\monitoring\Sensor\GitRepositorySensorException. */ namespace Drupal\monitoring\Sensor; /** - * Thrown if a disabled sensor is requested to be executed. + * Thrown if git repository error identified. */ class GitRepositorySensorException extends \InvalidArgumentException { diff --git a/lib/Drupal/monitoring/Sensor/SensorThresholds.php b/lib/Drupal/monitoring/Sensor/SensorThresholds.php index 9905a85..0b4692b 100644 --- a/lib/Drupal/monitoring/Sensor/SensorThresholds.php +++ b/lib/Drupal/monitoring/Sensor/SensorThresholds.php @@ -46,16 +46,11 @@ abstract class SensorThresholds extends SensorConfigurable implements SensorThre ), '#default_value' => $type, '#ajax' => array( - 'callback' => 'monitoring_sensor_thresholds_ajax', - 'wrapper' => 'monitoring-sensor-thresholds', + 'callback' => 'monitoring_sensor_settings_ajax', + 'wrapper' => 'monitoring-sensor-settings-wrapper', ), ); - $form['thresholds']['intervals'] = array( - '#prefix' => '
', - '#suffix' => '
', - ); - foreach ($thresholds as $status => $threshold) { switch ($type) { diff --git a/lib/Drupal/monitoring/Sensor/Sensors/SensorGit.php b/lib/Drupal/monitoring/Sensor/Sensors/SensorGit.php index ff52b8f..20e4c10 100644 --- a/lib/Drupal/monitoring/Sensor/Sensors/SensorGit.php +++ b/lib/Drupal/monitoring/Sensor/Sensors/SensorGit.php @@ -7,6 +7,7 @@ namespace Drupal\monitoring\Sensor\Sensors; use Drupal\monitoring\Result\SensorResultInterface; +use Drupal\monitoring\Sensor\GitRepositorySensorException; use Drupal\monitoring\Sensor\SensorConfigurable; use Drupal\monitoring\Sensor\SensorExtendedInfoInterface; @@ -16,20 +17,50 @@ use Drupal\monitoring\Sensor\SensorExtendedInfoInterface; class SensorGit extends SensorConfigurable implements SensorExtendedInfoInterface { protected $cmdOutput; + protected $repositoryPath; /** * {@inheritdoc} */ public function runSensor(SensorResultInterface $result) { - $this->cmdOutput = $this->exec($this->info->getSetting('cmd')); - $result->setSensorExpectedValue(0); + // First check if we have a valid repository path. + if ($this->getRepositoryPath($this->info->getSetting('repo_path')) == NULL) { + $result->setSensorStatus(SensorResultInterface::STATUS_UNKNOWN); + $result->setSensorMessage('The provided path %path does not exists or is not a git repository.', + array('%path' => $this->info->getSetting('repo_path'))); + return; + } + + if ($branch = $this->info->getSetting('branch')) { + try { + $this->checkBranch($branch); + $result->addSensorStatusMessage('On branch @branch', array('@branch' => $branch)); + } + catch (GitRepositorySensorException $e) { + $result->addSensorStatusMessage($e->getMessage()); + $result->setSensorStatus(SensorResultInterface::STATUS_CRITICAL); + } + } + elseif ($tag = $this->info->getSetting('tag')) { + try { + $this->checkTag($tag); + $result->addSensorStatusMessage('On branch @tag', array('@tag' => $tag)); + } + catch (GitRepositorySensorException $e) { + $result->addSensorStatusMessage($e->getMessage()); + $result->setSensorStatus(SensorResultInterface::STATUS_CRITICAL); + } + } + + $this->cmdOutput = $this->execAtGitRoot('git status --porcelain'); if (!empty($this->cmdOutput)) { - $result->setSensorValue(count(explode("\n", $this->cmdOutput))); - $result->addSensorStatusMessage('Files in unexpected state'); + $unexpected_files_count = count(explode("\n", $this->cmdOutput)); + $result->setSensorValue($unexpected_files_count); + $result->addSensorStatusMessage('@count files in unexpected state', array('@count' => $unexpected_files_count)); $result->setSensorStatus(SensorResultInterface::STATUS_CRITICAL); } - else { + elseif ($result->isOk()) { $result->setSensorValue(0); $result->addSensorStatusMessage('Git repository clean'); $result->setSensorStatus(SensorResultInterface::STATUS_OK); @@ -47,34 +78,47 @@ class SensorGit extends SensorConfigurable implements SensorExtendedInfoInterfac '#description' => t('Path of the repository relative to the Drupal root. Leave empty if the repository root is the same as Drupal root.'), '#default_value' => $this->info->getSetting('repo_path'), ); - - $form['info'] = array( - '#type' => 'fieldset', - '#title' => t('Local repository git info'), - '#collapsed' => TRUE, - '#collapsible' => TRUE, - ); - $form['info']['status'] = array( - '#type' => 'item', - '#title' => t('Status'), - '#markup' => '
' . $this->exec('git status') . '
', + $form['submit_repo_path'] = array( + '#type' => 'button', + '#value' => t('Update repository path'), + '#submit' => array('monitoring_sensor_settings_form_submit'), + '#validate' => array('monitoring_sensor_settings_form_validate'), + '#ajax' => array( + 'callback' => 'monitoring_sensor_settings_ajax', + 'wrapper' => 'monitoring-sensor-settings-wrapper', + ), ); - $form['info']['available_branches'] = array( - '#type' => 'item', - '#title' => t('Available branches'), - '#markup' => '
' . $this->exec('git branch') . '
', - ); - $form['info']['available_tags'] = array( - '#type' => 'item', - '#title' => t('Available tags'), - '#markup' => '
' . $this->exec('git tag') . '
', + + if (isset($form_state['values'][monitoring_sensor_settings_key($this->getSensorName())]['repo_path'])) { + $repo_path = $form_state['values'][monitoring_sensor_settings_key($this->getSensorName())]['repo_path']; + } + else { + $repo_path = $this->info->getSetting('repo_path'); + } + + if ($this->getRepositoryPath($repo_path) == NULL) { + $form['missing_repo_path_info'] = array( + '#type' => 'markup', + '#markup' => '
' . t('To proceed with sensor configuration provide a valid path to the git repository you want to monitor.') . '
' + ); + } + + $form['branch'] = array( + '#type' => 'select', + '#title' => t('Git branch'), + '#description' => t('Select valid branch for the current deployment.'), + '#options' => $this->getBranches(), + '#empty_option' => '--', + '#default_value' => $this->info->getSetting('branch'), ); - $form['cmd'] = array( - '#type' => 'textarea', - '#title' => t('Command'), - '#description' => t('Enter one or more shell commands that will check the local git repository status. Any output printed to console will escalate the sensor status to critical.'), - '#default_value' => $this->info->getSetting('cmd'), + $form['tag'] = array( + '#type' => 'select', + '#title' => t('Git tag'), + '#description' => t('Select valid tag for the current deployment.'), + '#options' => $this->getTags(), + '#empty_option' => '--', + '#default_value' => $this->info->getSetting('tag'), ); return $form; @@ -85,8 +129,8 @@ class SensorGit extends SensorConfigurable implements SensorExtendedInfoInterfac */ public function settingsFormValidate($form, &$form_state) { $path = $form_state['values'][monitoring_sensor_settings_key($this->getSensorName())]['repo_path']; - if (!file_exists($path)) { - form_set_error('repo_path', t('The provided path %path does not exists.', array('%path' => $path))); + if ($this->getRepositoryPath($path) == NULL) { + form_set_error('repo_path', t('The provided path %path does not exists or is not a git repository.', array('%path' => $path))); } } @@ -94,9 +138,7 @@ class SensorGit extends SensorConfigurable implements SensorExtendedInfoInterfac * {@inheritdoc} */ public function resultVerbose(SensorResultInterface $result) { - $output = 'CMD: ' . $this->exec($this->info->getSetting('cmd')); - $output .= "\n\n" . $this->cmdOutput; - return $output; + return format_string('Files in unexpected state:
@files
', array('@files' => $this->cmdOutput)); } /** @@ -108,9 +150,133 @@ class SensorGit extends SensorConfigurable implements SensorExtendedInfoInterfac * @return string * Shell command. */ - protected function exec($cmd) { - $repo_path = DRUPAL_ROOT . '/' . $this->info->getSetting('repo_path'); + protected function execAtGitRoot($cmd) { + $repo_path = escapeshellarg($this->getRepositoryPath($this->info->getSetting('repo_path'))); return trim(shell_exec("cd $repo_path\n$cmd 2>&1")); } + /** + * Checks if the current branch is the same as the provided. + * + * @param string $branch + * Expected branch name. + * + * @throws \Drupal\monitoring\Sensor\GitRepositorySensorException + * In case the current branch name is equal to the expected. + */ + protected function checkBranch($branch) { + $current_branch = NULL; + foreach ($this->getBranches() as $key => $value) { + if (strpos($value, '*') !== FALSE) { + $current_branch = $key; + } + } + + if ($current_branch != $branch) { + throw new GitRepositorySensorException(format_string('Unexpected branch @current expecting @expected', array('@expected' => $branch, '@current' => $current_branch))); + } + } + + /** + * Checks if the current tag is the same as the provided. + * + * @param string $tag + * Expected tag name. + * + * @throws \Drupal\monitoring\Sensor\GitRepositorySensorException + * In case the current tag name is equal to the expected. + */ + protected function checkTag($tag) { + $current_tag = NULL; + foreach ($this->getTags() as $key => $value) { + if (strpos($value, '*') !== FALSE) { + $current_tag = $key; + } + } + + if ($current_tag != $tag) { + throw new GitRepositorySensorException(format_string('Unexpected tag @current expecting @expected', array('@expected' => $tag, '@current' => $current_tag))); + } + } + + /** + * Gets absolute repository path. + * + * @param string $path + * Path relative to Drupal root. + * + * @return null|string + * If the resulting path is a valid git repository the absolute path to the + * repository is returned. + * If the resulting path is not a valid git repository NULL is returned. + */ + protected function getRepositoryPath($path) { + if (empty($this->repositoryPath)) { + $repo_path = DRUPAL_ROOT . '/' . $path; + $cmd = "cd " . escapeshellarg($repo_path) . "\ngit status 2>&1"; + if (file_exists($repo_path) && strpos(shell_exec($cmd), '# On branch') !== FALSE) { + $this->repositoryPath = $repo_path; + } + else { + $this->repositoryPath = NULL; + } + } + + return $this->repositoryPath; + } + + /** + * Gets branches of the current git repository. + * + * @return array + * List of branches where key as well as the value are branch names. The + * value of the current branch is prefixed with *. + */ + protected function getBranches() { + $branches = array(); + + $cmd_output = $this->execAtGitRoot('git branch'); + if (strpos($cmd_output, 'fatal') !== FALSE) { + return $branches; + } + + foreach (explode("\n", $cmd_output) as $branch) { + if (strpos($branch, '*') === 0) { + $key = trim(str_replace('*', '', $branch)); + } + else { + $key = trim($branch); + } + $branches[$key] = trim($branch); + } + return $branches; + } + + /** + * Gets tags of the current git repository. + * + * @return array + * List of tags where key as well as the value are tag names. The + * value of the current tag is prefixed with *. + */ + protected function getTags() { + $tags = array(); + + $cmd_output = $this->execAtGitRoot('git branch'); + if (strpos($cmd_output, 'fatal') !== FALSE) { + return $tags; + } + + foreach (explode("\n", $cmd_output) as $tag) { + if (strpos($tag, '*') === 0) { + $key = trim(str_replace('*', '', $tag)); + } + else { + $key = trim($tag); + } + $tags[$key] = trim($tag); + } + return $tags; + } + } diff --git a/monitoring.admin.inc b/monitoring.admin.inc index 53c52f1..50fa75b 100644 --- a/monitoring.admin.inc +++ b/monitoring.admin.inc @@ -275,6 +275,8 @@ function monitoring_sensor_settings_form($form, &$form_state, SensorInfo $sensor ); $form[$form_key] = $sensor->settingsForm($form[$form_key], $form_state); + $form[$form_key]['#prefix'] = '
'; + $form[$form_key]['#suffix'] = '
'; $form['actions']['submit'] = array( '#type' => 'submit', @@ -479,8 +481,8 @@ function monitoring_settings_form($form, &$form_state) { /** * Ajax callback for threshold settings. */ -function monitoring_sensor_thresholds_ajax($form, &$form_state) { +function monitoring_sensor_settings_ajax($form, &$form_state) { $sensor_name = $form_state['sensor_name']; $form_key = monitoring_sensor_settings_key($sensor_name); - return $form[$form_key]['thresholds']['intervals']; + return $form[$form_key]; } diff --git a/monitoring.drush.inc b/monitoring.drush.inc index 0ba0233..1c10e35 100644 --- a/monitoring.drush.inc +++ b/monitoring.drush.inc @@ -44,7 +44,7 @@ function monitoring_drush_command() { 'force' => 'If the sensor execution should be forced in case cached result is available.', 'output' => 'The output format. Currently "table" and "json" available. Defaults to "table".', 'expand' => 'Relevant only for the json output. Currently "sensor_info" value supported.', - 'show-exec-time' => 'Relevant for the table output listing all results. Will expand the table with execution time info.' + 'show-execAtGitRoot-time' => 'Relevant for the table output listing all results. Will expand the table with execution time info.' ), 'examples' => array( 'drush monitoring-result monitoring_git' => 'Runs sensor to monitor the git status.', @@ -180,7 +180,7 @@ function monitoring_drush_run($sensor_name = NULL) { $verbose = (bool) drush_get_option('verbose'); $output = drush_get_option('output', 'table'); $expand = drush_get_option('expand'); - $show_exec_time = drush_get_option('show-exec-time'); + $show_exec_time = drush_get_option('show-execAtGitRoot-time'); try { $sensor_names = array(); diff --git a/monitoring.module b/monitoring.module index b12c8b5..83510eb 100644 --- a/monitoring.module +++ b/monitoring.module @@ -9,6 +9,7 @@ use Drupal\monitoring\Sensor\NonExistingSensorException; use Drupal\monitoring\Sensor\SensorInfo; use Drupal\monitoring\Result\SensorResultInterface; use Drupal\monitoring\Sensor\SensorManager; +use Drupal\monitoring\Sensor\Sensors\SensorGit; use Drupal\monitoring\SensorRunner; use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException; diff --git a/monitoring.monitoring_sensors.inc b/monitoring.monitoring_sensors.inc index dc9d3bc..8298cb2 100644 --- a/monitoring.monitoring_sensors.inc +++ b/monitoring.monitoring_sensors.inc @@ -16,7 +16,8 @@ function monitoring_monitoring_sensor_info() { // source base managed by git. 'enabled' => FALSE, 'repo_path' => NULL, - 'cmd' => 'git status --porcelain', + 'branch' => 'master', + 'tag' => NULL, ), ); $info['monitoring_disappeared_sensors'] = array(