Compare commits
113 Commits
Author | SHA1 | Date |
---|---|---|
Michal Kleiner | 9881c03c22 | |
Guy Sartorelli | e8517ebfb0 | |
Michal Kleiner | 28ca036345 | |
Guy Sartorelli | 16caad30b2 | |
Maxime Rainville | 6f6c30bdcc | |
Guy Sartorelli | f918209e35 | |
Maxime Rainville | 714de64252 | |
Steve Boyd | d9d245a0da | |
Guy Sartorelli | 9d5f8fce7d | |
Guy Sartorelli | f12698f47f | |
Guy Sartorelli | 6f0d1c84f2 | |
Guy Sartorelli | 15a8bca241 | |
Steve Boyd | 94ff5b621c | |
Will Rossiter | 0e4867f736 | |
Steve Boyd | 7e59b7e88b | |
Steve Boyd | e4b8934148 | |
Guy Sartorelli | 737ac0d8ed | |
Steve Boyd | 3378dc7a99 | |
Steve Boyd | 58caa67c96 | |
Steve Boyd | 7be4c0dbf3 | |
Guy Sartorelli | 7ba4acc4b2 | |
Steve Boyd | a76c3b89ea | |
Guy Sartorelli | 7f6c03b0cb | |
Will Rossiter | 3e3907d14c | |
Guy Sartorelli | d3d20e5539 | |
Steve Boyd | b7554a5fc9 | |
Daniel Hensby | cb13f4ec12 | |
Steve Boyd | 3902a1d688 | |
Steve Boyd | aa1311fa01 | |
Steve Boyd | 47b184df5d | |
Maxime Rainville | 94fa4ee1f0 | |
Maxime Rainville | 3f2fbdaea0 | |
Steve Boyd | 05dd7bc0a7 | |
Steve Boyd | f6e001bf55 | |
Maxime Rainville | e5b77ff325 | |
Steve Boyd | 029078d157 | |
Steve Boyd | f14fe7c40d | |
Maxime Rainville | 585b6564b2 | |
Maxime Rainville | dd037c8f50 | |
Steve Boyd | d29a062562 | |
Steve Boyd | 2c4d32af6b | |
Robbie Averill | 7c0b7b0ca7 | |
Steve Boyd | b765ec87c1 | |
Steve Boyd | 56dab108c9 | |
Steve Boyd | b01567767c | |
Maxime Rainville | e19e476cf4 | |
Maxime Rainville | dfd9c15d56 | |
Maxime Rainville | 23104b77aa | |
Steve Boyd | 1aa18e7ced | |
Maxime Rainville | 08409bb9ca | |
Damian Mooyman | b2f6e06795 | |
Garion Herman | 0685dfad75 | |
Garion Herman | 5f2591a5c1 | |
Garion Herman | 2bbebc321e | |
Sam Minnée | 9829476dae | |
Garion Herman | 49d7d5f2a3 | |
Robbie Averill | f347d1d5c0 | |
Serge Latyntcev | c490c4e5a5 | |
Aaron Carlino | d8452c681a | |
Serge Latyntcev | e757c9477c | |
Damian Mooyman | 6fe0b1889e | |
Damian Mooyman | 40050e9ab4 | |
Robbie Averill | efd996d618 | |
Robbie Averill | 922d810a3a | |
Damian Mooyman | a54400681f | |
Dylan Wagstaff | c5783a2450 | |
Frank Mullenger | da9eddc0e1 | |
Frank Mullenger | 8f7f8570f2 | |
Frank Mullenger | 578c73e6ce | |
Robbie Averill | 128ddb56ed | |
Frank Mullenger | ace6c51aae | |
Frank Mullenger | 540710ab0f | |
Frank Mullenger | 2674beb94a | |
Frank Mullenger | 90f0556ca4 | |
Frank Mullenger | 27994e51fa | |
Guy Marriott | 986bc370b9 | |
Frank Mullenger | c1bfd5640d | |
Frank Mullenger | df59195634 | |
Guy Marriott | f74c371e16 | |
Guy Marriott | 41763c6042 | |
Guy Marriott | f8d35639a0 | |
Frank Mullenger | e323674adf | |
Frank Mullenger | da6a8a8ee4 | |
Frank Mullenger | 9940c5e8ab | |
Frank Mullenger | 1771a6ef11 | |
Robbie Averill | 05ac221c2f | |
Dylan Wagstaff | ed61c6bb3a | |
Robbie Averill | 014c23804f | |
Robbie Averill | f6652c2b37 | |
Robbie Averill | 161f0aefaf | |
Dylan Wagstaff | b89aefaa20 | |
Daniel Hensby | 152c11638b | |
Robbie Averill | e6e5ac96df | |
Dylan Wagstaff | 1ce832f859 | |
Robbie Averill | b1f5ef184b | |
Robbie Averill | bf5076f2df | |
Dylan Wagstaff | 227b64cbcc | |
Robbie Averill | 0e84848c3d | |
Daniel Hensby | ab610bb55e | |
Dylan Wagstaff | fb0bddec2c | |
Daniel Hensby | 1fe68e7d4a | |
Robbie Averill | a68532cef0 | |
Robbie Averill | 2131840da4 | |
Andrew Aitken-Fincham | 9dcfc4f337 | |
Daniel Hensby | 7204c4792d | |
Andrew Aitken-Fincham | 5b4adb286e | |
Daniel Hensby | 04b7cb2ff1 | |
Andrew Aitken-Fincham | 1e727bf8c6 | |
Sam Minnee | 5897559cd2 | |
Robbie Averill | b9af2d0734 | |
Robbie Averill | 745a9f93ef | |
Robbie Averill | 254ed4801f | |
Daniel Hensby | 1f6ba594a6 |
|
@ -1,6 +1,7 @@
|
|||
/tests export-ignore
|
||||
/docs export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.travis.yml export-ignore
|
||||
/.scrutinizer.yml export-ignore
|
||||
/codecov.yml export-ignore
|
||||
/phpunit.xml.dist export-ignore
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
name: CI
|
||||
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1
|
|
@ -0,0 +1,16 @@
|
|||
name: Dispatch CI
|
||||
|
||||
on:
|
||||
# At 1:20 PM UTC, only on Wednesday and Thursday
|
||||
schedule:
|
||||
- cron: '20 13 * * 3,4'
|
||||
|
||||
jobs:
|
||||
dispatch-ci:
|
||||
name: Dispatch CI
|
||||
# Only run cron on the silverstripe account
|
||||
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dispatch CI
|
||||
uses: silverstripe/gha-dispatch-ci@v1
|
|
@ -0,0 +1,17 @@
|
|||
name: Keepalive
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# The 4th of every month at 10:50am UTC
|
||||
schedule:
|
||||
- cron: '50 10 4 * *'
|
||||
|
||||
jobs:
|
||||
keepalive:
|
||||
name: Keepalive
|
||||
# Only run cron on the silverstripe account
|
||||
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Keepalive
|
||||
uses: silverstripe/gha-keepalive@v1
|
|
@ -1,9 +0,0 @@
|
|||
inherit: true
|
||||
|
||||
checks:
|
||||
php:
|
||||
code_rating: true
|
||||
duplication: true
|
||||
|
||||
filter:
|
||||
paths: [code/*, tests/*]
|
35
.travis.yml
35
.travis.yml
|
@ -1,35 +0,0 @@
|
|||
# See https://github.com/silverstripe-labs/silverstripe-travis-support for setup details
|
||||
|
||||
sudo: false
|
||||
|
||||
language: php
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
- 5.5
|
||||
|
||||
env:
|
||||
- DB=MYSQL CORE_RELEASE=3.5
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=3
|
||||
- php: 7.0
|
||||
env: DB=MYSQL CORE_RELEASE=3.6
|
||||
- php: 7.1
|
||||
env: DB=PGSQL CORE_RELEASE=3.6 COVERAGE=1
|
||||
|
||||
before_script:
|
||||
- composer self-update || true
|
||||
- git clone git://github.com/silverstripe-labs/silverstripe-travis-support.git ~/travis-support
|
||||
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
|
||||
- cd ~/builds/ss
|
||||
- composer install
|
||||
|
||||
script:
|
||||
- if [[ $COVERAGE ]]; then vendor/bin/phpunit --coverage-clover coverage.xml environmentcheck/tests; fi
|
||||
- if [[ ! $COVERAGE ]]; then vendor/bin/phpunit environmentcheck/tests; fi
|
||||
|
||||
after_success:
|
||||
- if [[ $COVERAGE ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
|
|
@ -0,0 +1,29 @@
|
|||
mappings:
|
||||
EnvironmentCheck: SilverStripe\EnvironmentCheck\EnvironmentCheck
|
||||
EnvironmentChecker: SilverStripe\EnvironmentCheck\EnvironmentChecker
|
||||
EnvironmentCheckSuite: SilverStripe\EnvironmentCheck\EnvironmentCheckSuite
|
||||
EnvironmentCheckSuiteResult: SilverStripe\EnvironmentCheck\EnvironmentCheckSuiteResult
|
||||
DatabaseCheck: SilverStripe\EnvironmentCheck\Checks\DatabaseCheck
|
||||
ExternalURLCheck: SilverStripe\EnvironmentCheck\Checks\ExternalURLCheck
|
||||
FileAccessibilityAndValidationCheck: SilverStripe\EnvironmentCheck\Checks\FileAccessibilityAndValidationCheck
|
||||
FileAgeCheck: SilverStripe\EnvironmentCheck\Checks\FileAgeCheck
|
||||
FileWriteableCheck: SilverStripe\EnvironmentCheck\Checks\FileWriteableCheck
|
||||
HasClassCheck: SilverStripe\EnvironmentCheck\Checks\HasClassCheck
|
||||
HasFunctionCheck: SilverStripe\EnvironmentCheck\Checks\HasFunctionCheck
|
||||
SMTPConnectCheck: SilverStripe\EnvironmentCheck\Checks\SMTPConnectCheck
|
||||
SolrIndexCheck: SilverStripe\EnvironmentCheck\Checks\SolrIndexCheck
|
||||
URLCheck: SilverStripe\EnvironmentCheck\Checks\URLCheck
|
||||
DevCheckController: SilverStripe\EnvironmentCheck\Controllers\DevCheckController
|
||||
DevHealthController: SilverStripe\EnvironmentCheck\Controllers\DevHealthController
|
||||
EnvironmentCheckerTest: SilverStripe\EnvironmentCheck\Tests\EnvironmentCheckerTest
|
||||
EnvironmentCheckerTest_CheckNoErrors: SilverStripe\EnvironmentCheck\Tests\EnvironmentCheckerTest_CheckNoErrors
|
||||
EnvironmentCheckerTest_CheckWarnings: SilverStripe\EnvironmentCheck\Tests\EnvironmentCheckerTest_CheckWarnings
|
||||
EnvironmentCheckerTest_CheckErrors: SilverStripe\EnvironmentCheck\Tests\EnvironmentCheckerTest_CheckErrors
|
||||
DatabaseCheckTest: SilverStripe\EnvironmentCheck\Tests\Checks\DatabaseCheckTest
|
||||
ExternalURLCheckTest: SilverStripe\EnvironmentCheck\Tests\Checks\ExternalURLCheckTest
|
||||
FileWritableCheckTest: SilverStripe\EnvironmentCheck\Tests\Checks\FileWritableCheckTest
|
||||
HasClassCheckTest: SilverStripe\EnvironmentCheck\Tests\Checks\HasClassCheckTest
|
||||
HasFunctionCheckTest: SilverStripe\EnvironmentCheck\Tests\Checks\HasFunctionCheckTest
|
||||
URLCheckTest: SilverStripe\EnvironmentCheck\Tests\Checks\URLCheckTest
|
||||
DevCheckControllerTest: SilverStripe\EnvironmentCheck\Tests\Controllers\DevCheckControllerTest
|
||||
DevHealthControllerTest: SilverStripe\EnvironmentCheck\Tests\Controllers\DevHealthControllerTest
|
|
@ -1,10 +1,7 @@
|
|||
# SilverStripe Environment Checker Module
|
||||
# Silverstripe Environment Checker Module
|
||||
|
||||
[![Build Status](https://travis-ci.org/silverstripe/silverstripe-environmentcheck.svg?branch=master)](https://travis-ci.org/silverstripe/silverstripe-environmentcheck)
|
||||
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-environmentcheck/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-environmentcheck/?branch=master)
|
||||
[![codecov](https://codecov.io/gh/silverstripe/comment-notifications/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/comment-notifications)
|
||||
[![Packagist](https://img.shields.io/packagist/v/silverstripe/environmentcheck.svg)](https://packagist.org/packages/silverstripe/environmentalcheck)
|
||||
[![License](http://img.shields.io/packagist/l/silverstripe/environmentcheck.svg)](LICENSE.md)
|
||||
[![CI](https://github.com/silverstripe/silverstripe-environmentcheck/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-environmentcheck/actions/workflows/ci.yml)
|
||||
[![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
|
||||
|
||||
This module adds an API for running environment checks to your API.
|
||||
|
||||
|
@ -12,6 +9,13 @@ This module adds an API for running environment checks to your API.
|
|||
* `dev/check` - An admin-only URL that performs a more comprehensive set of checks. This could be tied to a deployment system, for example.
|
||||
* `dev/check/<suite>` - Check a specific suite (admin only)
|
||||
|
||||
## Requirements
|
||||
|
||||
* Silverstripe 4.x
|
||||
* PHP 7.3
|
||||
|
||||
For Silverstripe 3.x support, please use a `1.x` tagged release.
|
||||
|
||||
## Aren't these just unit tests?
|
||||
|
||||
Almost, but not really. Environment checks differ from unit tests in two important ways:
|
||||
|
@ -29,11 +33,13 @@ You'll also need to run `/dev/build`.
|
|||
|
||||
### Activating Directly
|
||||
|
||||
Register checks in your own `_config.php` - see the `_config.php` in this module for some defaults.
|
||||
Register checks in your own `_config.php` - see the `_config.php` in this module for some defaults. Don't forget to
|
||||
either use a fully-qualified (namespaced) class name for `EnvironmentCheckSuite`, or `use` (import) the namespaced class
|
||||
first.
|
||||
|
||||
```php
|
||||
EnvironmentCheckSuite::register('health', 'DatabaseCheck', "Can we connect to the database?");
|
||||
EnvironmentCheckSuite::register('check', 'URLCheck("")', "Is the homepage accessible?");
|
||||
EnvironmentCheckSuite::register('health', 'DatabaseCheck', 'Can we connect to the database?');
|
||||
EnvironmentCheckSuite::register('check', 'URLCheck("")', 'Is the homepage accessible?');
|
||||
```
|
||||
|
||||
### Activating Via Config
|
||||
|
@ -41,7 +47,7 @@ EnvironmentCheckSuite::register('check', 'URLCheck("")', "Is the homepage access
|
|||
Register your checks on the `EnvironmentCheckSuite`. The same named check may be used multiple times.
|
||||
|
||||
```yaml
|
||||
EnvironmentCheckSuite:
|
||||
SilverStripe\EnvironmentCheck\EnvironmentCheckSuite:
|
||||
registered_checks:
|
||||
db:
|
||||
definition: 'DatabaseCheck("Page")'
|
||||
|
@ -61,7 +67,7 @@ You can also disable checks configured this way. This is handy if you want to ov
|
|||
by some other module. Just set the "state" property of the check to "disabled" like this:
|
||||
|
||||
```yaml
|
||||
EnvironmentCheckSuite:
|
||||
SilverStripe\EnvironmentCheck\EnvironmentCheckSuite:
|
||||
registered_checks:
|
||||
db:
|
||||
state: disabled
|
||||
|
@ -76,33 +82,38 @@ EnvironmentCheckSuite:
|
|||
* `HasClassCheck`: Check that the given class exists.
|
||||
This can be used to check that PHP modules or features are installed.
|
||||
* `FileWriteableCheck`: Check that the given file is writeable.
|
||||
* `FileAccessibilityAndValidationCheck`: Check that a given file is accessible and optionally matches a given format.
|
||||
* `FileAgeCheck`: Checks for the maximum age of one or more files or folders.
|
||||
Useful for files which should be frequently auto-generated,
|
||||
Useful for files which should be frequently auto-generated,
|
||||
like static caches, as well as for backup files and folders.
|
||||
* `ExternalURLCheck`: Checks that one or more URLs are reachable via HTTP.
|
||||
* `SMTPConnectCheck`: Checks if the SMTP connection configured through PHP.ini works as expected.
|
||||
* `SolrIndexCheck`: Checks if the Solr cores of given class are available.
|
||||
* `SessionCheck`: Checks that a given URL does not generate a session.
|
||||
* `CacheHeadersCheck`: Check cache headers in response for directives that must either be included or excluded as well
|
||||
checking for existence of ETag.
|
||||
* `EnvTypeCheck`: Checks environment type, dev and test should not be used on production environments.
|
||||
|
||||
## Monitoring Checks
|
||||
|
||||
Checks will return an appropriate HTTP status code, so are easy to hook into common uptime montoring
|
||||
solutions like pingdom.com.
|
||||
solutions like pingdom.com.
|
||||
|
||||
You can also have the environment checker email results with the following configuration:
|
||||
|
||||
```yml
|
||||
EnvironmentChecker:
|
||||
```yaml
|
||||
SilverStripe\EnvironmentCheck\EnvironmentChecker:
|
||||
email_results: true
|
||||
to_email_address: support@test.com
|
||||
from_email_address: admin@test.com
|
||||
```
|
||||
|
||||
Errors can be logged via the standard SilverStripe logging. Each check will cause an individual log entry.
|
||||
You can choose to enable logging separately for warnings and errors,
|
||||
identified through the result of `EnvironmentCheck->check()`.
|
||||
Errors can be logged via the standard Silverstripe PSR-3 compatible logging. Each check will cause an individual log
|
||||
entry. You can choose to enable logging separately for warnings and errors, identified through the
|
||||
result of `EnvironmentCheck->check()`.
|
||||
|
||||
```yml
|
||||
EnvironmentChecker:
|
||||
```yaml
|
||||
SilverStripe\EnvironmentCheck\EnvironmentChecker:
|
||||
log_results_warning: true
|
||||
log_results_error: true
|
||||
```
|
||||
|
@ -114,73 +125,90 @@ trying to access it on a live or test environment, it will respond with a 403 HT
|
|||
an administrator on the site.
|
||||
|
||||
You may wish to have an automated service check `dev/check` periodically, but not want to open it up for public access.
|
||||
You can enable basic authentication by defining the following in your environment:
|
||||
You can enable basic authentication by defining the following in your environment (`.env` file):
|
||||
|
||||
define('ENVCHECK_BASICAUTH_USERNAME', 'test');
|
||||
define('ENVCHECK_BASICAUTH_PASSWORD', 'password');
|
||||
```
|
||||
ENVCHECK_BASICAUTH_USERNAME="test"
|
||||
ENVCHECK_BASICAUTH_PASSWORD="password"
|
||||
```
|
||||
|
||||
Now if you access `dev/check` in a browser it will pop up a basic auth popup, and if the submitted username and password
|
||||
match the ones defined the username and password defined in the environment, access will be granted to the page.
|
||||
|
||||
## Adding more checks
|
||||
|
||||
To add more checks, you should put additional `EnvironmentCheckSuite::register` calls into your `_config.php`. See the `_config.php` file of this module for examples.
|
||||
To add more checks, you should put additional `EnvironmentCheckSuite::register` calls into your `_config.php`.
|
||||
See the `_config.php` file of this module for examples.
|
||||
|
||||
```php
|
||||
EnvironmentCheckSuite::register('check', 'HasFunctionCheck("curl_init")', "Does PHP have CURL support?");
|
||||
EnvironmentCheckSuite::register('check', 'HasFunctionCheck("imagecreatetruecolor")', "Does PHP have GD2 support?");
|
||||
```
|
||||
|
||||
:::php
|
||||
EnvironmentCheckSuite::register('check', 'HasFunctionCheck("curl_init")', "Does PHP have CURL support?");
|
||||
EnvironmentCheckSuite::register('check', 'HasFunctionCheck("imagecreatetruecolor")', "Does PHP have GD2 support?");
|
||||
|
||||
The first argument is the name of the check suite. There are two built-in check suites, "health", and "check", corresponding to the `health/check` and `dev/check` URLs. If you wish, you can create your own check suites and execute them on other URLs. You can also add a check to more than one suite by passing the first argument as an array.
|
||||
|
||||
The module comes bundled with a few checks in `DefaultHealthChecks.php`. However, to test your own application, you probably want to write custom checks.
|
||||
To test your own application, you probably want to write custom checks:
|
||||
|
||||
* Implement the `EnvironmentCheck` interface
|
||||
* Implement the `SilverStripe\EnvironmentCheck\EnvironmentCheck` interface
|
||||
* Define the `check()` function, which returns a 2 element array:
|
||||
* The first element is one of `EnvironmentCheck::OK`, `EnvironmentCheck::WARNING`, `EnvironmentCheck::ERROR`, depending on the status of the check
|
||||
* The second element is a string describing the response.
|
||||
|
||||
Here is a simple example of how you might create a check to test your own code. In this example, we are checking that an instance of the `MyGateway` class will return "foo" when `call()` is called on it. Testing interfaces with 3rd party systems is a common use case for custom environment checks.
|
||||
|
||||
:::php
|
||||
class MyGatewayCheck implements EnvironmentCheck {
|
||||
protected $checkTable;
|
||||
```php
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
class MyGatewayCheck implements EnvironmentCheck
|
||||
{
|
||||
protected $checkTable;
|
||||
|
||||
function check()
|
||||
{
|
||||
$g = new \MyGateway;
|
||||
|
||||
$response = $g->call();
|
||||
$expectedResponse = 'foo';
|
||||
|
||||
if($response == null) {
|
||||
return array(EnvironmentCheck::ERROR, "MyGateway didn't return a response");
|
||||
} else if($response != $expectedResponse) {
|
||||
return array(EnvironmentCheck::WARNING, "MyGateway returned unexpected response $response");
|
||||
}
|
||||
return array(EnvironmentCheck::OK, '');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
function check() {
|
||||
$g = new MyGateway;
|
||||
|
||||
$response = $g->call();
|
||||
$expectedResponse = "foo";
|
||||
|
||||
if($response == null) {
|
||||
return array(EnvironmentCheck::ERROR, "MyGateway didn't return a response");
|
||||
} else if($response != $expectedResponse) {
|
||||
return array(EnvironmentCheck::WARNING, "MyGateway returned unexpected response $response");
|
||||
} else {
|
||||
return array(EnvironmentCheck::OK, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Once you have created your custom check class, don't forget to register it in a check suite
|
||||
|
||||
:::php
|
||||
EnvironmentCheckSuite::register('check', 'MyGatewayCheck', "Can I connect to the gateway?");
|
||||
|
||||
```php
|
||||
EnvironmentCheckSuite::register('check', 'MyGatewayCheck', 'Can I connect to the gateway?');
|
||||
```
|
||||
|
||||
### Using other environment check suites
|
||||
|
||||
If you want to use the same UI as `health/check` and dev/check, you can create an `EnvironmentChecker` object. This class is a `RequestHandler` and so can be returned from an action handler. The first argument to the `EnvironmentChecker` constructor is the suite name. For example:
|
||||
If you want to use the same UI as `health/check` and `dev/check`, you can create an `EnvironmentChecker` object. This class is a `RequestHandler` and so can be returned from an action handler. The first argument to the `EnvironmentChecker` constructor is the suite name. For example:
|
||||
|
||||
class DevHealth extends Controller {
|
||||
function index() {
|
||||
$e = new EnvironmentChecker('health', 'Site health');
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
|
||||
If you wish to embed an environment check suite in another, you can use the following call.
|
||||
```php
|
||||
use SilverStripe\Control\Controller;
|
||||
|
||||
class DevHealth extends Controller
|
||||
{
|
||||
function index()
|
||||
{
|
||||
$e = new EnvironmentChecker('health', 'Site health');
|
||||
return $e;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If you wish to embed an environment check suite in another, you can use the following call:
|
||||
|
||||
```php
|
||||
$result = EnvironmentCheckSuite::inst('health')->run();
|
||||
```
|
||||
|
||||
$result = EnvironmentCheckSuite::inst("health")->run();
|
||||
|
||||
`$result` will contain a `EnvironmentCheckSuiteResult` object
|
||||
|
||||
* `$result->ShouldPass()`: Return a boolean of whether or not the tests passed.
|
||||
|
@ -197,4 +225,4 @@ All methods, with `public` visibility, are part of the public API. All other met
|
|||
|
||||
## Reporting Issues
|
||||
|
||||
Please [create an issue](http://github.com/silverstripe-labs/silverstripe-environmentcheck/issues) for any bugs you've found, or features you're missing.
|
||||
Please [create an issue](http://github.com/silverstripe/silverstripe-environmentcheck/issues) for any bugs you've found, or features you're missing.
|
89
_config.php
89
_config.php
|
@ -1,21 +1,70 @@
|
|||
<?php
|
||||
|
||||
// // These power dev/health, which can be used by load balancers and other such systems
|
||||
// EnvironmentCheckSuite::register('health', 'DatabaseCheck');
|
||||
|
||||
// // These power dev/check, which is used for diagnostics and for deployment
|
||||
// EnvironmentCheckSuite::register('check', 'DatabaseCheck("Member")', "Is the database accessible?");
|
||||
// EnvironmentCheckSuite::register('check', 'URLCheck("")', "Is the homepage accessible?");
|
||||
|
||||
// EnvironmentCheckSuite::register('check', 'HasFunctionCheck("curl_init")', "Does PHP have CURL support?");
|
||||
// EnvironmentCheckSuite::register('check', 'HasFunctionCheck("imagecreatetruecolor")', "Does PHP have GD2 support?");
|
||||
// EnvironmentCheckSuite::register('check', 'HasFunctionCheck("xml_set_object")', "Does PHP have XML support?");
|
||||
// EnvironmentCheckSuite::register('check', 'HasFunctionCheck("token_get_all")', "Does PHP have tokenizer support?");
|
||||
// EnvironmentCheckSuite::register('check', 'HasFunctionCheck("iconv")', "Does PHP have iconv support?");
|
||||
// EnvironmentCheckSuite::register('check', 'HasFunctionCheck("hash")', "Does PHP have hash support?");
|
||||
// EnvironmentCheckSuite::register('check', 'HasFunctionCheck("session_start")', "Does PHP have session support?");
|
||||
// EnvironmentCheckSuite::register('check', 'HasClassCheck("DOMDocument")', "Does PHP have DOMDocument support?");
|
||||
|
||||
// EnvironmentCheckSuite::register('check', 'FileWriteableCheck("assets")', "Is assets/ writeable?");
|
||||
// EnvironmentCheckSuite::register('check', 'FileWriteableCheck("' . TEMP_FOLDER . '")', "Is the temp folder writeable?");
|
||||
|
||||
//
|
||||
//use SilverStripe\EnvironmentCheck\EnvironmentCheckSuite;
|
||||
//
|
||||
//// These power dev/health, which can be used by load balancers and other such systems
|
||||
//EnvironmentCheckSuite::register('health', 'DatabaseCheck');
|
||||
//
|
||||
//// These power dev/check, which is used for diagnostics and for deployment
|
||||
//EnvironmentCheckSuite::register('check', 'DatabaseCheck("Member")', "Is the database accessible?");
|
||||
//EnvironmentCheckSuite::register('check', 'URLCheck("")', "Is the homepage accessible?");
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasFunctionCheck("curl_init")',
|
||||
// "Does PHP have CURL support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasFunctionCheck("imagecreatetruecolor")',
|
||||
// "Does PHP have GD2 support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasFunctionCheck("xml_set_object")',
|
||||
// "Does PHP have XML support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasFunctionCheck("token_get_all")',
|
||||
// "Does PHP have tokenizer support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasFunctionCheck("iconv")',
|
||||
// "Does PHP have iconv support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasFunctionCheck("hash")',
|
||||
// "Does PHP have hash support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasFunctionCheck("session_start")',
|
||||
// "Does PHP have session support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'HasClassCheck("DOMDocument")',
|
||||
// "Does PHP have DOMDocument support?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'FileWriteableCheck("assets")',
|
||||
// "Is assets/ writeable?"
|
||||
//);
|
||||
//
|
||||
//EnvironmentCheckSuite::register(
|
||||
// 'check',
|
||||
// 'FileWriteableCheck("' . TEMP_FOLDER . '")',
|
||||
// "Is the temp folder writeable?"
|
||||
//);
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
Name: environmentcheckinjector
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
CacheHeadersCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck
|
||||
DatabaseCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\DatabaseCheck
|
||||
EnvTypeCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\EnvTypeCheck
|
||||
ExternalURLCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\ExternalURLCheck
|
||||
FileAccessibilityAndValidationCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\FileAccessibilityAndValidationCheck
|
||||
FileAgeCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\FileAgeCheck
|
||||
FileWriteableCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\FileWriteableCheck
|
||||
HasClassCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\HasClassCheck
|
||||
HasFunctionCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\HasFunctionCheck
|
||||
SMTPConnectCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\SMTPConnectCheck
|
||||
SolrIndexCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\SolrIndexCheck
|
||||
URLCheck:
|
||||
class: SilverStripe\EnvironmentCheck\Checks\URLCheck
|
||||
EnvCheckClient:
|
||||
factory: 'SilverStripe\EnvironmentCheck\Services\ClientFactory'
|
||||
constructor:
|
||||
timeout: 10.0
|
||||
|
||||
SilverStripe\EnvironmentCheck\Checks\SessionCheck:
|
||||
dependencies:
|
||||
client: '%$EnvCheckClient'
|
||||
SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck:
|
||||
dependencies:
|
||||
client: '%$EnvCheckClient'
|
|
@ -1,9 +1,25 @@
|
|||
---
|
||||
Name: environmentcheckroutes
|
||||
After: 'framework/*','cms/*'
|
||||
---
|
||||
Director:
|
||||
SilverStripe\Control\Director:
|
||||
rules:
|
||||
'health/check': 'DevHealthController'
|
||||
'dev/check/$Suite': 'DevCheckController'
|
||||
'health/check': 'Silverstripe\EnvironmentCheck\Controllers\DevHealthController'
|
||||
'dev/check/$Suite': 'Silverstripe\EnvironmentCheck\Controllers\DevCheckController'
|
||||
|
||||
SilverStripe\Dev\DevelopmentAdmin:
|
||||
registered_controllers:
|
||||
check:
|
||||
controller: Silverstripe\EnvironmentCheck\Controllers\DevCheckController
|
||||
links:
|
||||
check: 'Run registered environment checks and display their status'
|
||||
|
||||
---
|
||||
Name: environmentcheckroutes-dev_urls-confirmation-exceptions
|
||||
After:
|
||||
- 'dev_urls-confirmation-middleware'
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
DevUrlsConfirmationMiddleware:
|
||||
properties:
|
||||
Bypasses:
|
||||
- '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartsWith("dev/check")'
|
||||
|
|
17
changelog.md
17
changelog.md
|
@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [2.1.0]
|
||||
|
||||
* Add new checks:
|
||||
* Environment type
|
||||
* Session
|
||||
* Cache Headers
|
||||
* Fix basic auth
|
||||
* Add unit tests
|
||||
* PSR-2 Conformance
|
||||
|
||||
## [2.0.0]
|
||||
|
||||
* Minimum versions required:
|
||||
* SilverStripe 4.x
|
||||
* PHP 5.6+
|
||||
* PHP 7 supported
|
||||
|
||||
## [1.2.0]
|
||||
|
||||
* Fix broken badges
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
<?php
|
||||
|
||||
class DevCheckController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $allowed_actions = array(
|
||||
'index'
|
||||
);
|
||||
|
||||
/**
|
||||
* Permission code to check for access to this controller.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $permission = 'ADMIN';
|
||||
|
||||
/**
|
||||
* @param SS_HTTPRequest $request
|
||||
*
|
||||
* @return EnvironmentChecker
|
||||
*
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
*/
|
||||
public function index($request)
|
||||
{
|
||||
$suite = 'check';
|
||||
|
||||
if ($name = $request->param('Suite')) {
|
||||
$suite = $name;
|
||||
}
|
||||
|
||||
$checker = new EnvironmentChecker($suite, 'Environment status');
|
||||
$checker->init($this->config()->permission);
|
||||
|
||||
return $checker;
|
||||
}
|
||||
}
|
|
@ -1,292 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Provides an interface for checking the given EnvironmentCheckSuite.
|
||||
*/
|
||||
class EnvironmentChecker extends RequestHandler
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $url_handlers = array(
|
||||
'' => 'index',
|
||||
);
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $checkSuiteName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $errorCode = 500;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private static $to_email_address = null;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private static $from_email_address = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $email_results = false;
|
||||
|
||||
/**
|
||||
* @var bool Log results via {@link SS_Log}
|
||||
*/
|
||||
private static $log_results_warning = false;
|
||||
|
||||
/**
|
||||
* @var int Maps to {@link Zend_Log} levels. Defaults to Zend_Log::WARN
|
||||
*/
|
||||
private static $log_results_warning_level = 4;
|
||||
|
||||
/**
|
||||
* @var bool Log results via {@link SS_Log}
|
||||
*/
|
||||
private static $log_results_error = false;
|
||||
|
||||
/**
|
||||
* @var int Maps to {@link Zend_Log} levels. Defaults to Zend_Log::ALERT
|
||||
*/
|
||||
private static $log_results_error_level = 1;
|
||||
|
||||
/**
|
||||
* @param string $checkSuiteName
|
||||
* @param string $title
|
||||
*/
|
||||
public function __construct($checkSuiteName, $title)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->checkSuiteName = $checkSuiteName;
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $permission
|
||||
*
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
*/
|
||||
public function init($permission = 'ADMIN')
|
||||
{
|
||||
// if the environment supports it, provide a basic auth challenge and see if it matches configured credentials
|
||||
if (defined('ENVCHECK_BASICAUTH_USERNAME') && defined('ENVCHECK_BASICAUTH_PASSWORD')) {
|
||||
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])) {
|
||||
// authenticate the input user/pass with the configured credentials
|
||||
if (
|
||||
!(
|
||||
$_SERVER['PHP_AUTH_USER'] == ENVCHECK_BASICAUTH_USERNAME
|
||||
&& $_SERVER['PHP_AUTH_PW'] == ENVCHECK_BASICAUTH_PASSWORD
|
||||
)
|
||||
) {
|
||||
$response = new SS_HTTPResponse(null, 401);
|
||||
$response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\"");
|
||||
// Exception is caught by RequestHandler->handleRequest() and will halt further execution
|
||||
$e = new SS_HTTPResponse_Exception(null, 401);
|
||||
$e->setResponse($response);
|
||||
throw $e;
|
||||
}
|
||||
} else {
|
||||
$response = new SS_HTTPResponse(null, 401);
|
||||
$response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\"");
|
||||
// Exception is caught by RequestHandler->handleRequest() and will halt further execution
|
||||
$e = new SS_HTTPResponse_Exception(null, 401);
|
||||
$e->setResponse($response);
|
||||
throw $e;
|
||||
}
|
||||
} else {
|
||||
if (!$this->canAccess(null, $permission)) {
|
||||
return $this->httpError(403);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null|int|Member $member
|
||||
* @param string $permission
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
*/
|
||||
public function canAccess($member = null, $permission = "ADMIN")
|
||||
{
|
||||
if (!$member) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
if (!$member) {
|
||||
$member = BasicAuth::requireLogin('Environment Checker', $permission, false);
|
||||
}
|
||||
|
||||
// We allow access to this controller regardless of live-status or ADMIN permission only
|
||||
// if on CLI. Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
|
||||
if (
|
||||
Director::isDev()
|
||||
|| Director::is_cli()
|
||||
|| empty($permission)
|
||||
|| Permission::checkMember($member, $permission)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extended access checks.
|
||||
// "Veto" style, return NULL to abstain vote.
|
||||
$canExtended = null;
|
||||
$results = $this->extend('canAccess', $member);
|
||||
if ($results && is_array($results)) {
|
||||
if (!min($results)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SS_HTTPResponse
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$response = new SS_HTTPResponse;
|
||||
$result = EnvironmentCheckSuite::inst($this->checkSuiteName)->run();
|
||||
|
||||
if (!$result->ShouldPass()) {
|
||||
$response->setStatusCode($this->errorCode);
|
||||
}
|
||||
|
||||
$resultText = $result->customise(array(
|
||||
"URL" => Director::absoluteBaseURL(),
|
||||
"Title" => $this->title,
|
||||
"Name" => $this->checkSuiteName,
|
||||
"ErrorCode" => $this->errorCode,
|
||||
))->renderWith("EnvironmentChecker");
|
||||
|
||||
if ($this->config()->email_results && !$result->ShouldPass()) {
|
||||
$email = new Email($this->config()->from_email_address, $this->config()->to_email_address, $this->title, $resultText);
|
||||
$email->send();
|
||||
}
|
||||
|
||||
// Optionally log errors and warnings individually
|
||||
foreach ($result->Details() as $detail) {
|
||||
if ($this->config()->log_results_warning && $detail->StatusCode == EnvironmentCheck::WARNING) {
|
||||
$this->log(
|
||||
sprintf('EnvironmentChecker warning at "%s" check. Message: %s', $detail->Check, $detail->Message),
|
||||
$this->config()->log_results_warning_level
|
||||
);
|
||||
} elseif ($this->config()->log_results_error && $detail->StatusCode == EnvironmentCheck::ERROR) {
|
||||
$this->log(
|
||||
sprintf('EnvironmentChecker error at "%s" check. Message: %s', $detail->Check, $detail->Message),
|
||||
$this->config()->log_results_error_level
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// output the result as JSON if requested
|
||||
if (
|
||||
$this->getRequest()->getExtension() == 'json'
|
||||
|| strpos($this->getRequest()->getHeader('Accept'), 'application/json') !== false
|
||||
) {
|
||||
$response->setBody($result->toJSON());
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response->setBody($resultText);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $message
|
||||
* @param int $level
|
||||
*/
|
||||
public function log($message, $level)
|
||||
{
|
||||
SS_Log::log($message, $level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the HTTP status code that should be returned when there's an error.
|
||||
*
|
||||
* @param int $errorCode
|
||||
*/
|
||||
public function setErrorCode($errorCode)
|
||||
{
|
||||
$this->errorCode = $errorCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param string $from
|
||||
*/
|
||||
public static function set_from_email_address($from)
|
||||
{
|
||||
Deprecation::notice('2.0', 'Use config API instead');
|
||||
Config::inst()->update('EnvironmentChecker', 'from_email_address', $from);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return null|string
|
||||
*/
|
||||
public static function get_from_email_address()
|
||||
{
|
||||
Deprecation::notice('2.0', 'Use config API instead');
|
||||
return Config::inst()->get('EnvironmentChecker', 'from_email_address');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param string $to
|
||||
*/
|
||||
public static function set_to_email_address($to)
|
||||
{
|
||||
Deprecation::notice('2.0', 'Use config API instead');
|
||||
Config::inst()->update('EnvironmentChecker', 'to_email_address', $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return null|string
|
||||
*/
|
||||
public static function get_to_email_address()
|
||||
{
|
||||
Deprecation::notice('2.0', 'Use config API instead');
|
||||
return Config::inst()->get('EnvironmentChecker', 'to_email_address');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @param bool $results
|
||||
*/
|
||||
public static function set_email_results($results)
|
||||
{
|
||||
Deprecation::notice('2.0', 'Use config API instead');
|
||||
Config::inst()->update('EnvironmentChecker', 'email_results', $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_email_results()
|
||||
{
|
||||
Deprecation::notice('2.0', 'Use config API instead');
|
||||
return Config::inst()->get('EnvironmentChecker', 'email_results');
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Check the availability of all Solr indexes of given class.
|
||||
*
|
||||
* If there are no indexes of given class found, the returned status will still be "OK".
|
||||
*/
|
||||
class SolrIndexCheck implements EnvironmentCheck
|
||||
{
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
protected $indexClass;
|
||||
|
||||
/**
|
||||
* @param string $indexClass Limit the index checks to the specified class and all its subclasses.
|
||||
*/
|
||||
public function __construct($indexClass = null)
|
||||
{
|
||||
$this->indexClass = $indexClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$brokenCores = array();
|
||||
|
||||
if (!class_exists('Solr')) {
|
||||
return array(
|
||||
EnvironmentCheck::ERROR,
|
||||
'Class `Solr` not found. Is the fulltextsearch module installed?'
|
||||
);
|
||||
}
|
||||
|
||||
$service = Solr::service();
|
||||
foreach (Solr::get_indexes($this->indexClass) as $index) {
|
||||
$core = $index->getIndexName();
|
||||
if (!$service->coreIsActive($core)) {
|
||||
$brokenCores[] = $core;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($brokenCores)) {
|
||||
return array(
|
||||
EnvironmentCheck::ERROR,
|
||||
'The following indexes are unavailable: ' . implode($brokenCores, ', ')
|
||||
);
|
||||
}
|
||||
|
||||
return array(EnvironmentCheck::OK, 'Expected indexes are available.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
comment: false
|
|
@ -1,31 +1,41 @@
|
|||
{
|
||||
"name": "silverstripe/environmentcheck",
|
||||
"description": "Provides an API for building environment tests",
|
||||
"license": "BSD-3-Clause",
|
||||
"type": "silverstripe-module",
|
||||
"keywords": ["silverstripe", "testing", "environment", "check"],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Will Rossiter",
|
||||
"email": "will@fullscreen.io"
|
||||
},
|
||||
{
|
||||
"name": "Sam Minnee",
|
||||
"email": "sam@silverstripe.com"
|
||||
}
|
||||
],
|
||||
"require":
|
||||
{
|
||||
"silverstripe/framework": "~3.1"
|
||||
},
|
||||
"require-dev":
|
||||
{
|
||||
"hafriedlander/phockito": "dev-master"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"license": "BSD-3-Clause"
|
||||
}
|
||||
"name": "silverstripe/environmentcheck",
|
||||
"description": "Provides an API for building environment tests",
|
||||
"license": "BSD-3-Clause",
|
||||
"type": "silverstripe-vendormodule",
|
||||
"keywords": [
|
||||
"silverstripe",
|
||||
"testing",
|
||||
"environment",
|
||||
"check"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Will Rossiter",
|
||||
"email": "will@fullscreen.io"
|
||||
},
|
||||
{
|
||||
"name": "Sam Minnee",
|
||||
"email": "sam@silverstripe.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.4 || ^8.0",
|
||||
"silverstripe/framework": "^4.10",
|
||||
"silverstripe/versioned": "^1.0",
|
||||
"guzzlehttp/guzzle": "^6.3.3 || ^7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"extra": [],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SilverStripe\\EnvironmentCheck\\": "src/",
|
||||
"SilverStripe\\EnvironmentCheck\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2016, SilverStripe Limited
|
||||
Copyright (c) 2017, SilverStripe Limited
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ruleset name="SilverStripe">
|
||||
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
|
||||
<rule ref="PSR2" >
|
||||
<!-- Current exclusions -->
|
||||
<exclude name="PSR1.Methods.CamelCapsMethodName" />
|
||||
</rule>
|
||||
</ruleset>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="vendor/silverstripe/framework/tests/bootstrap.php" colors="true">
|
||||
|
||||
<testsuites>
|
||||
<testsuite name="Default">
|
||||
<directory>tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/.</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">tests/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
|
||||
</phpunit>
|
|
@ -0,0 +1,207 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\ORM\ValidationResult;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
use SilverStripe\EnvironmentCheck\Traits\Fetcher;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Check cache headers for any response, can specify directives that must be included and
|
||||
* also must be excluded from Cache-Control headers in response. Also checks for
|
||||
* existence of ETag.
|
||||
*
|
||||
* @example SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck("/",["must-revalidate", "max-age=120"],["no-store"])
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class CacheHeadersCheck implements EnvironmentCheck
|
||||
{
|
||||
use Fetcher;
|
||||
|
||||
/**
|
||||
* Settings that must be included in the Cache-Control header
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $mustInclude = [];
|
||||
|
||||
/**
|
||||
* Settings that must be excluded in the Cache-Control header
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $mustExclude = [];
|
||||
|
||||
/**
|
||||
* Result to keep track of status and messages for all checks, reuses
|
||||
* ValidationResult for convenience.
|
||||
*
|
||||
* @var ValidationResult
|
||||
*/
|
||||
protected $result;
|
||||
|
||||
/**
|
||||
* Set up with URL, arrays of header settings to check.
|
||||
*
|
||||
* @param string $url
|
||||
* @param array $mustInclude Settings that must be included in Cache-Control
|
||||
* @param array $mustExclude Settings that must be excluded in Cache-Control
|
||||
*/
|
||||
public function __construct($url = '', $mustInclude = [], $mustExclude = [])
|
||||
{
|
||||
$this->setURL($url);
|
||||
$this->mustInclude = $mustInclude;
|
||||
$this->mustExclude = $mustExclude;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that correct caching headers are present.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
// Using a validation result to capture messages
|
||||
$this->result = new ValidationResult();
|
||||
|
||||
$response = $this->client->get($this->getURL());
|
||||
$fullURL = $this->getURL();
|
||||
if ($response === null) {
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
"Cache headers check request failed for $fullURL",
|
||||
];
|
||||
}
|
||||
|
||||
//Check that Etag exists
|
||||
$this->checkEtag($response);
|
||||
|
||||
// Check Cache-Control settings
|
||||
$this->checkCacheControl($response);
|
||||
|
||||
if ($this->result->isValid()) {
|
||||
return [
|
||||
EnvironmentCheck::OK,
|
||||
$this->getMessage(),
|
||||
];
|
||||
} else {
|
||||
// @todo Ability to return a warning
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
$this->getMessage(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collate messages from ValidationResult so that it is clear which parts
|
||||
* of the check passed and which failed.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getMessage()
|
||||
{
|
||||
$ret = '';
|
||||
// Filter good messages
|
||||
$goodTypes = [ValidationResult::TYPE_GOOD, ValidationResult::TYPE_INFO];
|
||||
$good = array_filter(
|
||||
$this->result->getMessages() ?? [],
|
||||
function ($val, $key) use ($goodTypes) {
|
||||
if (in_array($val['messageType'], $goodTypes ?? [])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
if (!empty($good)) {
|
||||
$ret .= "GOOD: " . implode('; ', array_column($good ?? [], 'message')) . " ";
|
||||
}
|
||||
|
||||
// Filter bad messages
|
||||
$badTypes = [ValidationResult::TYPE_ERROR, ValidationResult::TYPE_WARNING];
|
||||
$bad = array_filter(
|
||||
$this->result->getMessages() ?? [],
|
||||
function ($val, $key) use ($badTypes) {
|
||||
if (in_array($val['messageType'], $badTypes ?? [])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
ARRAY_FILTER_USE_BOTH
|
||||
);
|
||||
if (!empty($bad)) {
|
||||
$ret .= "BAD: " . implode('; ', array_column($bad ?? [], 'message'));
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that ETag header exists
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @return void
|
||||
*/
|
||||
private function checkEtag(ResponseInterface $response)
|
||||
{
|
||||
$eTag = $response->getHeaderLine('ETag');
|
||||
$fullURL = Controller::join_links(Director::absoluteBaseURL(), $this->url);
|
||||
|
||||
if ($eTag) {
|
||||
$this->result->addMessage(
|
||||
"$fullURL includes an Etag header in response",
|
||||
ValidationResult::TYPE_GOOD
|
||||
);
|
||||
return;
|
||||
}
|
||||
$this->result->addError(
|
||||
"$fullURL is missing an Etag header",
|
||||
ValidationResult::TYPE_WARNING
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the correct header settings are either included or excluded.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @return void
|
||||
*/
|
||||
private function checkCacheControl(ResponseInterface $response)
|
||||
{
|
||||
$cacheControl = $response->getHeaderLine('Cache-Control');
|
||||
$vals = array_map('trim', explode(',', $cacheControl ?? ''));
|
||||
$fullURL = Controller::join_links(Director::absoluteBaseURL(), $this->url);
|
||||
|
||||
// All entries from must contain should be present
|
||||
if ($this->mustInclude == array_intersect($this->mustInclude ?? [], $vals)) {
|
||||
$matched = implode(",", $this->mustInclude);
|
||||
$this->result->addMessage(
|
||||
"$fullURL includes all settings: {$matched}",
|
||||
ValidationResult::TYPE_GOOD
|
||||
);
|
||||
} else {
|
||||
$missing = implode(",", array_diff($this->mustInclude ?? [], $vals));
|
||||
$this->result->addError(
|
||||
"$fullURL is excluding some settings: {$missing}"
|
||||
);
|
||||
}
|
||||
|
||||
// All entries from must exclude should not be present
|
||||
if (empty(array_intersect($this->mustExclude ?? [], $vals))) {
|
||||
$missing = implode(",", $this->mustExclude);
|
||||
$this->result->addMessage(
|
||||
"$fullURL excludes all settings: {$missing}",
|
||||
ValidationResult::TYPE_GOOD
|
||||
);
|
||||
} else {
|
||||
$matched = implode(",", array_intersect($this->mustExclude ?? [], $vals));
|
||||
$this->result->addError(
|
||||
"$fullURL is including some settings: {$matched}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\ORM\DB;
|
||||
|
||||
/**
|
||||
* Check that the connection to the database is working, by ensuring that the table exists and that
|
||||
* the table contains some records.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class DatabaseCheck implements EnvironmentCheck
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $checkTable;
|
||||
|
||||
/**
|
||||
|
@ -13,28 +23,28 @@ class DatabaseCheck implements EnvironmentCheck
|
|||
*
|
||||
* @param string $checkTable
|
||||
*/
|
||||
public function __construct($checkTable = "Member")
|
||||
public function __construct($checkTable = 'Member')
|
||||
{
|
||||
$this->checkTable = $checkTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
if (!DB::getConn()->hasTable($this->checkTable)) {
|
||||
return array(EnvironmentCheck::ERROR, "$this->checkTable not present in the database");
|
||||
if (!DB::get_schema()->hasTable($this->checkTable)) {
|
||||
return [EnvironmentCheck::ERROR, "$this->checkTable not present in the database"];
|
||||
}
|
||||
|
||||
$count = DB::query("SELECT COUNT(*) FROM \"$this->checkTable\"")->value();
|
||||
|
||||
|
||||
if ($count > 0) {
|
||||
return array(EnvironmentCheck::OK, "");
|
||||
} else {
|
||||
return array(EnvironmentCheck::WARNING, "$this->checkTable queried ok but has no records");
|
||||
return [EnvironmentCheck::OK, ''];
|
||||
}
|
||||
|
||||
return [EnvironmentCheck::WARNING, "$this->checkTable queried ok but has no records"];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Check whether the environment setting is safe. Useful for live sites where a
|
||||
* non "Live" setting might disclose sensitive information.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class EnvTypeCheck implements EnvironmentCheck
|
||||
{
|
||||
/**
|
||||
* Check the environment setting.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$envSetting = Director::get_environment_type();
|
||||
switch ($envSetting) {
|
||||
case 'live':
|
||||
return [
|
||||
EnvironmentCheck::OK,
|
||||
"Env setting is 'live'",
|
||||
];
|
||||
// Fallthrough
|
||||
default:
|
||||
case 'dev':
|
||||
case 'test':
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
"Env setting is '{$envSetting}' and may disclose information",
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,19 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Checks that one or more URLs are reachable via HTTP.
|
||||
* Note that the HTTP connectivity can just be verified from the server to the remote URL,
|
||||
* it can still fail if the URL in question is requested by the client, e.g. through an iframe.
|
||||
*
|
||||
*
|
||||
* Requires curl to present, so ensure to check it before with the following:
|
||||
* <code>EnvironmentCheckSuite::register('check', 'HasFunctionCheck("curl_init")', "Does PHP have CURL support?");</code>
|
||||
* <code>
|
||||
* EnvironmentCheckSuite::register(
|
||||
* 'check',
|
||||
* 'HasFunctionCheck("curl_init")',
|
||||
* "Does PHP have CURL support?"
|
||||
* );
|
||||
* </code>
|
||||
*/
|
||||
class ExternalURLCheck implements EnvironmentCheck
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $urls = array();
|
||||
protected $urls = [];
|
||||
|
||||
/**
|
||||
* @var Int Timeout in seconds.
|
||||
|
@ -27,13 +37,13 @@ class ExternalURLCheck implements EnvironmentCheck
|
|||
public function __construct($urls, $timeout = 15)
|
||||
{
|
||||
if ($urls) {
|
||||
$this->urls = explode(' ', $urls);
|
||||
$this->urls = explode(' ', $urls ?? '');
|
||||
}
|
||||
$this->timeout = $timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
@ -41,18 +51,18 @@ class ExternalURLCheck implements EnvironmentCheck
|
|||
{
|
||||
$urls = $this->getURLs();
|
||||
|
||||
$chs = array();
|
||||
$chs = [];
|
||||
foreach ($urls as $url) {
|
||||
$ch = curl_init();
|
||||
$chs[] = $ch;
|
||||
curl_setopt_array($ch, $this->getCurlOpts($url));
|
||||
curl_setopt_array($ch, $this->getCurlOpts($url) ?? []);
|
||||
}
|
||||
// Parallel execution for faster performance
|
||||
$mh = curl_multi_init();
|
||||
foreach ($chs as $ch) {
|
||||
curl_multi_add_handle($mh, $ch);
|
||||
}
|
||||
|
||||
|
||||
$active = null;
|
||||
// Execute the handles
|
||||
do {
|
||||
|
@ -69,7 +79,7 @@ class ExternalURLCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
$hasError = false;
|
||||
$msgs = array();
|
||||
$msgs = [];
|
||||
foreach ($chs as $ch) {
|
||||
$url = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
@ -95,12 +105,12 @@ class ExternalURLCheck implements EnvironmentCheck
|
|||
curl_multi_remove_handle($mh, $ch);
|
||||
}
|
||||
curl_multi_close($mh);
|
||||
|
||||
|
||||
if ($hasError) {
|
||||
return array(EnvironmentCheck::ERROR, implode(', ', $msgs));
|
||||
} else {
|
||||
return array(EnvironmentCheck::OK, implode(', ', $msgs));
|
||||
return [EnvironmentCheck::ERROR, implode(', ', $msgs)];
|
||||
}
|
||||
|
||||
return [EnvironmentCheck::OK, implode(', ', $msgs)];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -108,13 +118,13 @@ class ExternalURLCheck implements EnvironmentCheck
|
|||
*/
|
||||
protected function getCurlOpts($url)
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
CURLOPT_URL => $url,
|
||||
CURLOPT_HEADER => 0,
|
||||
CURLOPT_RETURNTRANSFER => 1,
|
||||
CURLOPT_FAILONERROR => 1,
|
||||
CURLOPT_TIMEOUT => $this->timeout,
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
|
@ -1,22 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
/**
|
||||
* Checks for the accessibility and file type validation of one or more files or folders.
|
||||
*
|
||||
* Examples:
|
||||
* // Checks /assets/calculator_files has .json files and all files are valid json files.
|
||||
* EnvironmentCheckSuite::register('check', 'FileAccessibilityAndValidationCheck("' . BASE_PATH . '/assets/calculator_files/*.json",
|
||||
* "jsonValidate", '.FileAccessibilityAndValidationCheck::CHECK_ALL.')', 'Check a json file exist and are all valid json files'
|
||||
* );
|
||||
*
|
||||
* // Checks /assets/calculator_files/calculator.json exists and is valid json file.
|
||||
* EnvironmentCheckSuite::register('check', 'FileAccessibilityAndValidationCheck("' . BASE_PATH . '/assets/calculator_files/calculator.json",
|
||||
* "jsonValidate", '.FileAccessibilityAndValidationCheck::CHECK_SINGLE.')', 'Check a calculator.json exists and is valid json file'
|
||||
* EnvironmentCheckSuite::register(
|
||||
* 'check',
|
||||
* 'FileAccessibilityAndValidationCheck(
|
||||
* "' . BASE_PATH . '/assets/calculator_files/*.json",
|
||||
* "jsonValidate",
|
||||
* '.FileAccessibilityAndValidationCheck::CHECK_ALL.'
|
||||
* )',
|
||||
* 'Check a json file exist and are all valid json files'
|
||||
* );
|
||||
*
|
||||
* // Check only existence
|
||||
* EnvironmentCheckSuite::register('check', 'FileAccessibilityAndValidationCheck("' . BASE_PATH . '/assets/calculator_files/calculator.json")',
|
||||
* 'Check a calculator.json exists only'
|
||||
* // Checks /assets/calculator_files/calculator.json exists and is valid json file.
|
||||
* EnvironmentCheckSuite::register(
|
||||
* 'check',
|
||||
* 'FileAccessibilityAndValidationCheck(
|
||||
* "' . BASE_PATH . '/assets/calculator_files/calculator.json",
|
||||
* "jsonValidate",
|
||||
* '.FileAccessibilityAndValidationCheck::CHECK_SINGLE.'
|
||||
* )',
|
||||
* 'Check a calculator.json exists and is valid json file'
|
||||
* );
|
||||
*
|
||||
* // Check only existence
|
||||
* EnvironmentCheckSuite::register(
|
||||
* 'check',
|
||||
* 'FileAccessibilityAndValidationCheck("' . BASE_PATH . '/assets/calculator_files/calculator.json")',
|
||||
* 'Check a calculator.json exists only'
|
||||
* );
|
||||
*/
|
||||
class FileAccessibilityAndValidationCheck implements EnvironmentCheck
|
||||
|
@ -60,26 +79,26 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
|
|||
public function __construct($path, $fileTypeValidateFunc = 'noVidation', $checkType = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->fileTypeValidateFunc = ($fileTypeValidateFunc)? $fileTypeValidateFunc:'noVidation';
|
||||
$this->fileTypeValidateFunc = ($fileTypeValidateFunc)? $fileTypeValidateFunc : 'noVidation';
|
||||
$this->checkType = ($checkType) ? $checkType : self::CHECK_SINGLE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$origStage = Versioned::get_reading_mode();
|
||||
Versioned::set_reading_mode('Live');
|
||||
Versioned::set_reading_mode(Versioned::LIVE);
|
||||
|
||||
$files = $this->getFiles();
|
||||
if ($files) {
|
||||
$fileTypeValidateFunc = $this->fileTypeValidateFunc;
|
||||
if (method_exists($this, $fileTypeValidateFunc)) {
|
||||
$invalidFiles = array();
|
||||
$validFiles = array();
|
||||
if (method_exists($this, $fileTypeValidateFunc ?? '')) {
|
||||
$invalidFiles = [];
|
||||
$validFiles = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
if ($this->$fileTypeValidateFunc($file)) {
|
||||
|
@ -90,41 +109,49 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
// If at least one file was valid, count as passed
|
||||
if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles) < count($files)) {
|
||||
$validFileList = "\n";
|
||||
if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles ?? []) < count($files ?? [])) {
|
||||
$validFileList = PHP_EOL;
|
||||
foreach ($validFiles as $vf) {
|
||||
$validFileList .= $vf."\n";
|
||||
$validFileList .= $vf . PHP_EOL;
|
||||
}
|
||||
if ($fileTypeValidateFunc == 'noVidation') {
|
||||
$checkReturn = array(
|
||||
$checkReturn = [
|
||||
EnvironmentCheck::OK,
|
||||
sprintf('At least these file(s) accessible: %s', $validFileList)
|
||||
);
|
||||
];
|
||||
} else {
|
||||
$checkReturn = array(
|
||||
$checkReturn = [
|
||||
EnvironmentCheck::OK,
|
||||
sprintf('At least these file(s) passed file type validate function "%s": %s', $fileTypeValidateFunc, $validFileList)
|
||||
);
|
||||
sprintf(
|
||||
'At least these file(s) passed file type validate function "%s": %s',
|
||||
$fileTypeValidateFunc,
|
||||
$validFileList
|
||||
)
|
||||
];
|
||||
}
|
||||
} else {
|
||||
if (count($invalidFiles) == 0) {
|
||||
$checkReturn = array(EnvironmentCheck::OK, 'All files valideted');
|
||||
if (count($invalidFiles ?? []) == 0) {
|
||||
$checkReturn = [EnvironmentCheck::OK, 'All files valideted'];
|
||||
} else {
|
||||
$invalidFileList = "\n";
|
||||
$invalidFileList = PHP_EOL;
|
||||
foreach ($invalidFiles as $vf) {
|
||||
$invalidFileList .= $vf."\n";
|
||||
$invalidFileList .= $vf . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($fileTypeValidateFunc == 'noVidation') {
|
||||
$checkReturn = array(
|
||||
$checkReturn = [
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf('File(s) not accessible: %s', $invalidFileList)
|
||||
);
|
||||
];
|
||||
} else {
|
||||
$checkReturn = array(
|
||||
$checkReturn = [
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf('File(s) not passing the file type validate function "%s": %s', $fileTypeValidateFunc, $invalidFileList)
|
||||
);
|
||||
sprintf(
|
||||
'File(s) not passing the file type validate function "%s": %s',
|
||||
$fileTypeValidateFunc,
|
||||
$invalidFileList
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,12 +180,11 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
|
|||
*/
|
||||
private function jsonValidate($file)
|
||||
{
|
||||
$json = json_decode(file_get_contents($file));
|
||||
$json = json_decode(file_get_contents($file ?? '') ?? '');
|
||||
if (!$json) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -178,6 +204,6 @@ class FileAccessibilityAndValidationCheck implements EnvironmentCheck
|
|||
*/
|
||||
protected function getFiles()
|
||||
{
|
||||
return glob($this->path);
|
||||
return glob($this->path ?? '');
|
||||
}
|
||||
}
|
|
@ -1,23 +1,30 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||
|
||||
/**
|
||||
* Checks for the maximum age of one or more files or folders.
|
||||
* Useful for files which should be frequently auto-generated,
|
||||
* Useful for files which should be frequently auto-generated,
|
||||
* like static caches, as well as for backup files and folders.
|
||||
* Does NOT check for existence of a file (will silently fail).
|
||||
*
|
||||
* Examples:
|
||||
* // Checks that Requirements::combine_files() has regenerated files in the last 24h
|
||||
* EnvironmentCheckSuite::register(
|
||||
* 'check',
|
||||
* 'FileAgeCheck("' . ASSETS_PATH . '/_combined_files/*.js' . '", "-1 day", '>', " . FileAgeCheck::CHECK_ALL) . "'
|
||||
* 'check',
|
||||
* 'FileAgeCheck("' . ASSETS_PATH . '/_combined_files/*.js' . '", "-1 day", '>', " . FileAgeCheck::CHECK_ALL) . "'
|
||||
* );
|
||||
*
|
||||
*
|
||||
* // Checks that at least one backup folder has been created in the last 24h
|
||||
* EnvironmentCheckSuite::register(
|
||||
* 'check',
|
||||
* 'FileAgeCheck("' . BASE_PATH . '/../backups/*' . '", "-1 day", '>', " . FileAgeCheck::CHECK_SINGLE) . "'
|
||||
* 'check',
|
||||
* 'FileAgeCheck("' . BASE_PATH . '/../backups/*' . '", "-1 day", '>', " . FileAgeCheck::CHECK_SINGLE) . "'
|
||||
* );
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class FileAgeCheck implements EnvironmentCheck
|
||||
{
|
||||
|
@ -30,7 +37,7 @@ class FileAgeCheck implements EnvironmentCheck
|
|||
* @var int
|
||||
*/
|
||||
const CHECK_ALL = 2;
|
||||
|
||||
|
||||
/**
|
||||
* Absolute path to a file or folder, compatible with glob().
|
||||
*
|
||||
|
@ -83,16 +90,16 @@ class FileAgeCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$cutoffTime = strtotime($this->relativeAge, SS_Datetime::now()->Format('U'));
|
||||
$cutoffTime = strtotime($this->relativeAge ?? '', DBDatetime::now()->Format('U'));
|
||||
$files = $this->getFiles();
|
||||
$invalidFiles = array();
|
||||
$validFiles = array();
|
||||
$invalidFiles = [];
|
||||
$validFiles = [];
|
||||
$checkFn = $this->checkFn;
|
||||
$allValid = true;
|
||||
if ($files) {
|
||||
|
@ -104,31 +111,32 @@ class FileAgeCheck implements EnvironmentCheck
|
|||
} else {
|
||||
$invalidFiles[] = $file;
|
||||
if ($this->checkType == self::CHECK_ALL) {
|
||||
return array(
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf(
|
||||
'File "%s" doesn\'t match age check (compare %s: %s, actual: %s)',
|
||||
$file, $this->compareOperand, date('c', $cutoffTime), date('c', $fileTime)
|
||||
)
|
||||
);
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf(
|
||||
'File "%s" doesn\'t match age check (compare %s: %s, actual: %s)',
|
||||
$file,
|
||||
$this->compareOperand,
|
||||
date('c', $cutoffTime),
|
||||
date('c', $fileTime)
|
||||
)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If at least one file was valid, count as passed
|
||||
if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles) < count($files)) {
|
||||
return array(EnvironmentCheck::OK, '');
|
||||
} else {
|
||||
if (count($invalidFiles) == 0) {
|
||||
return array(EnvironmentCheck::OK, '');
|
||||
} else {
|
||||
return array(
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf('No files matched criteria (%s %s)', $this->compareOperand, date('c', $cutoffTime))
|
||||
);
|
||||
}
|
||||
if ($this->checkType == self::CHECK_SINGLE && count($invalidFiles ?? []) < count($files ?? [])) {
|
||||
return [EnvironmentCheck::OK, ''];
|
||||
}
|
||||
if (count($invalidFiles ?? []) == 0) {
|
||||
return [EnvironmentCheck::OK, ''];
|
||||
}
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf('No files matched criteria (%s %s)', $this->compareOperand, date('c', $cutoffTime))
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,6 +146,6 @@ class FileAgeCheck implements EnvironmentCheck
|
|||
*/
|
||||
protected function getFiles()
|
||||
{
|
||||
return glob($this->path);
|
||||
return glob($this->path ?? '');
|
||||
}
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Check that the given file is writable.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class FileWriteableCheck implements EnvironmentCheck
|
||||
{
|
||||
|
@ -19,7 +25,7 @@ class FileWriteableCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
|
@ -28,50 +34,54 @@ class FileWriteableCheck implements EnvironmentCheck
|
|||
if ($this->path[0] == '/') {
|
||||
$filename = $this->path;
|
||||
} else {
|
||||
$filename = BASE_PATH . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $this->path);
|
||||
$filename = BASE_PATH . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $this->path ?? '');
|
||||
}
|
||||
|
||||
if (file_exists($filename)) {
|
||||
$isWriteable = is_writeable($filename);
|
||||
|
||||
if (file_exists($filename ?? '')) {
|
||||
$isWriteable = is_writeable($filename ?? '');
|
||||
} else {
|
||||
$isWriteable = is_writeable(dirname($filename));
|
||||
$isWriteable = is_writeable(dirname($filename ?? ''));
|
||||
}
|
||||
|
||||
|
||||
if (!$isWriteable) {
|
||||
if (function_exists('posix_getgroups')) {
|
||||
$userID = posix_geteuid();
|
||||
$user = posix_getpwuid($userID);
|
||||
$user = posix_getpwuid($userID ?? 0);
|
||||
|
||||
$currentOwnerID = fileowner(file_exists($filename) ? $filename : dirname($filename));
|
||||
$currentOwner = posix_getpwuid($currentOwnerID);
|
||||
$currentOwnerID = fileowner(file_exists($filename ?? '') ? $filename : dirname($filename ?? ''));
|
||||
$currentOwner = posix_getpwuid($currentOwnerID ?? 0);
|
||||
|
||||
$message = "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe file is currently owned by '$currentOwner[name]'. ";
|
||||
$message = "User '$user[name]' needs to be able to write to this file:\n$filename\n\nThe file is "
|
||||
. "currently owned by '$currentOwner[name]'. ";
|
||||
|
||||
if ($user['name'] == $currentOwner['name']) {
|
||||
$message .= "We recommend that you make the file writeable.";
|
||||
$message .= 'We recommend that you make the file writeable.';
|
||||
} else {
|
||||
$groups = posix_getgroups();
|
||||
$groupList = array();
|
||||
$groupList = [];
|
||||
foreach ($groups as $group) {
|
||||
$groupInfo = posix_getgrgid($group);
|
||||
if (in_array($currentOwner['name'], $groupInfo['members'])) {
|
||||
$groupInfo = posix_getgrgid($group ?? 0);
|
||||
if (in_array($currentOwner['name'], $groupInfo['members'] ?? [])) {
|
||||
$groupList[] = $groupInfo['name'];
|
||||
}
|
||||
}
|
||||
if ($groupList) {
|
||||
$message .= " We recommend that you make the file group-writeable and change the group to one of these groups:\n - ". implode("\n - ", $groupList)
|
||||
$message .= " We recommend that you make the file group-writeable and change the group to "
|
||||
. "one of these groups:\n - " . implode("\n - ", $groupList)
|
||||
. "\n\nFor example:\nchmod g+w $filename\nchgrp " . $groupList[0] . " $filename";
|
||||
} else {
|
||||
$message .= " There is no user-group that contains both the web-server user and the owner of this file. Change the ownership of the file, create a new group, or temporarily make the file writeable by everyone during the install process.";
|
||||
$message .= " There is no user-group that contains both the web-server user and the owner "
|
||||
. "of this file. Change the ownership of the file, create a new group, or temporarily "
|
||||
. "make the file writeable by everyone during the install process.";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$message = "The webserver user needs to be able to write to this file:\n$filename";
|
||||
}
|
||||
|
||||
return array(EnvironmentCheck::ERROR, $message);
|
||||
|
||||
return [EnvironmentCheck::ERROR, $message];
|
||||
}
|
||||
|
||||
return array(EnvironmentCheck::OK,'');
|
||||
return [EnvironmentCheck::OK, ''];
|
||||
}
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Check that the given class exists.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class HasClassCheck implements EnvironmentCheck
|
||||
{
|
||||
|
@ -19,16 +25,15 @@ class HasClassCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
if (class_exists($this->className)) {
|
||||
return array(EnvironmentCheck::OK, 'Class ' . $this->className.' exists');
|
||||
} else {
|
||||
return array(EnvironmentCheck::ERROR, 'Class ' . $this->className.' doesn\'t exist');
|
||||
if (class_exists($this->className ?? '')) {
|
||||
return [EnvironmentCheck::OK, 'Class ' . $this->className.' exists'];
|
||||
}
|
||||
return [EnvironmentCheck::ERROR, 'Class ' . $this->className.' doesn\'t exist'];
|
||||
}
|
||||
}
|
|
@ -1,7 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Check that the given function exists.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class HasFunctionCheck implements EnvironmentCheck
|
||||
{
|
||||
|
@ -19,16 +25,15 @@ class HasFunctionCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
if (function_exists($this->functionName)) {
|
||||
return array(EnvironmentCheck::OK, $this->functionName.'() exists');
|
||||
} else {
|
||||
return array(EnvironmentCheck::ERROR, $this->functionName.'() doesn\'t exist');
|
||||
if (function_exists($this->functionName ?? '')) {
|
||||
return [EnvironmentCheck::OK, $this->functionName . '() exists'];
|
||||
}
|
||||
return [EnvironmentCheck::ERROR, $this->functionName . '() doesn\'t exist'];
|
||||
}
|
||||
}
|
|
@ -1,9 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Checks if the SMTP connection configured through PHP.ini works as expected.
|
||||
*
|
||||
* Only checks socket connection with HELO command, not actually sending the email.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class SMTPConnectCheck implements EnvironmentCheck
|
||||
{
|
||||
|
@ -35,7 +41,7 @@ class SMTPConnectCheck implements EnvironmentCheck
|
|||
if (!$this->host) {
|
||||
$this->host = 'localhost';
|
||||
}
|
||||
|
||||
|
||||
$this->port = ($port) ? $port : ini_get('smtp_port');
|
||||
if (!$this->port) {
|
||||
$this->port = 25;
|
||||
|
@ -45,29 +51,29 @@ class SMTPConnectCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$f = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout);
|
||||
$f = @fsockopen($this->host ?? '', $this->port ?? 0, $errno, $errstr, $this->timeout);
|
||||
if (!$f) {
|
||||
return array(
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf("Couldn't connect to SMTP on %s:%s (Error: %s %s)", $this->host, $this->port, $errno, $errstr)
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
fwrite($f, "HELO its_me\r\n");
|
||||
$response = fread($f, 26);
|
||||
if (substr($response, 0, 3) != '220') {
|
||||
return array(
|
||||
if (substr($response ?? '', 0, 3) != '220') {
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf("Invalid mail server response: %s", $response)
|
||||
);
|
||||
sprintf('Invalid mail server response: %s', $response)
|
||||
];
|
||||
}
|
||||
|
||||
return array(EnvironmentCheck::OK, '');
|
||||
return [EnvironmentCheck::OK, ''];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use SilverStripe\EnvironmentCheck\Traits\Fetcher;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Check that a given URL does not generate a session.
|
||||
*
|
||||
* @author Adrian Humphreys
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class SessionCheck implements EnvironmentCheck
|
||||
{
|
||||
use Fetcher;
|
||||
|
||||
/**
|
||||
* Set up check with URL
|
||||
*
|
||||
* @param string $url The route, excluding the domain
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct($url = '')
|
||||
{
|
||||
$this->setURL($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the response for URL does not create a session
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$response = $this->client->get($this->getURL());
|
||||
$cookie = $this->getCookie($response);
|
||||
$fullURL = $this->getURL();
|
||||
|
||||
if ($cookie) {
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
"Sessions are being set for {$fullURL} : Set-Cookie => " . $cookie,
|
||||
];
|
||||
}
|
||||
return [
|
||||
EnvironmentCheck::OK,
|
||||
"Sessions are not being created for {$fullURL} 👍",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get PHPSESSID or SECSESSID cookie set from the response if it exists.
|
||||
*
|
||||
* @param ResponseInterface $response
|
||||
* @return string|null Cookie contents or null if it doesn't exist
|
||||
*/
|
||||
public function getCookie(ResponseInterface $response)
|
||||
{
|
||||
$result = null;
|
||||
$cookies = $response->getHeader('Set-Cookie');
|
||||
|
||||
foreach ($cookies as $cookie) {
|
||||
if (strpos($cookie ?? '', 'SESSID') !== false) {
|
||||
$result = $cookie;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\FullTextSearch\Solr\Solr;
|
||||
use SilverStripe\FullTextSearch\Solr\SolrIndex;
|
||||
|
||||
/**
|
||||
* Check the availability of all Solr indexes
|
||||
*
|
||||
* If there are no indexes of given class found, the returned status will still be "OK".
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class SolrIndexCheck implements EnvironmentCheck
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$brokenCores = [];
|
||||
|
||||
if (!class_exists(Solr::class)) {
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
'Class `' . Solr::class . '` not found. Is the fulltextsearch module installed?'
|
||||
];
|
||||
}
|
||||
|
||||
$service = Solr::service();
|
||||
foreach (Solr::get_indexes() as $index) {
|
||||
/** @var SolrIndex $core */
|
||||
$core = $index->getIndexName();
|
||||
if (!$service->coreIsActive($core)) {
|
||||
$brokenCores[] = $core;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($brokenCores)) {
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
'The following indexes are unavailable: ' . implode(', ', $brokenCores)
|
||||
];
|
||||
}
|
||||
|
||||
return [EnvironmentCheck::OK, 'Expected indexes are available.'];
|
||||
}
|
||||
}
|
|
@ -1,9 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Checks;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Check that a given URL is functioning, by default, the homepage.
|
||||
*
|
||||
*
|
||||
* Note that Director::test() will be used rather than a CURL check.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class URLCheck implements EnvironmentCheck
|
||||
{
|
||||
|
@ -16,8 +23,8 @@ class URLCheck implements EnvironmentCheck
|
|||
* @var string
|
||||
*/
|
||||
protected $testString;
|
||||
|
||||
/*
|
||||
|
||||
/**
|
||||
* @param string $url The URL to check, relative to the site (homepage is '').
|
||||
* @param string $testString An optional piece of text to search for on the homepage.
|
||||
*/
|
||||
|
@ -28,31 +35,29 @@ class URLCheck implements EnvironmentCheck
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* {@inheritDoc}
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$response = Director::test($this->url);
|
||||
|
||||
if ($response->getStatusCode() != 200) {
|
||||
return array(
|
||||
return [
|
||||
EnvironmentCheck::ERROR,
|
||||
sprintf('Error retrieving "%s" (Code: %d)', $this->url, $response->getStatusCode())
|
||||
);
|
||||
} elseif ($this->testString && (strpos($response->getBody(), $this->testString) === false)) {
|
||||
return array(
|
||||
];
|
||||
} elseif ($this->testString && (strpos($response->getBody() ?? '', $this->testString ?? '') === false)) {
|
||||
return [
|
||||
EnvironmentCheck::WARNING,
|
||||
sprintf('Success retrieving "%s", but string "%s" not found', $this->url, $this->testString)
|
||||
);
|
||||
} else {
|
||||
return array(
|
||||
EnvironmentCheck::OK,
|
||||
sprintf('Success retrieving "%s"', $this->url)
|
||||
);
|
||||
];
|
||||
}
|
||||
return [
|
||||
EnvironmentCheck::OK,
|
||||
sprintf('Success retrieving "%s"', $this->url)
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Controllers;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentChecker;
|
||||
|
||||
/**
|
||||
* Class DevCheckController
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class DevCheckController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_actions = [
|
||||
'index'
|
||||
];
|
||||
|
||||
/**
|
||||
* Permission code to check for access to this controller.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $permission = 'ADMIN';
|
||||
|
||||
/**
|
||||
* @param HTTPRequest $request
|
||||
*
|
||||
* @return EnvironmentChecker
|
||||
*
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function index($request)
|
||||
{
|
||||
$suite = 'check';
|
||||
|
||||
if ($name = $request->param('Suite')) {
|
||||
$suite = $name;
|
||||
}
|
||||
|
||||
/** @var EnvironmentChecker */
|
||||
$checker = EnvironmentChecker::create($suite, 'Environment status')
|
||||
->setRequest($request)
|
||||
->setIncludeDetails(true);
|
||||
|
||||
$checker->init($this->config()->permission);
|
||||
|
||||
return $checker;
|
||||
}
|
||||
}
|
|
@ -1,23 +1,33 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Controllers;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentChecker;
|
||||
|
||||
/**
|
||||
* Class DevHealthController
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class DevHealthController extends Controller
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $allowed_actions = array(
|
||||
private static $allowed_actions = [
|
||||
'index'
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @return EnvironmentChecker
|
||||
*
|
||||
* @throws SS_HTTPResponse_Exception
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
// health check does not require permission to run
|
||||
|
||||
$checker = new EnvironmentChecker('health', 'Site health');
|
||||
$checker->setErrorCode(500);
|
||||
|
|
@ -1,18 +1,22 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* Interface for environment checks
|
||||
*
|
||||
*
|
||||
* An environment check is a test that can be performed on a live environment. They differ from
|
||||
* unit tests in that they are designed to check the state of the environment/server, rather than
|
||||
* the code.
|
||||
*
|
||||
*
|
||||
* Environment checks should *never* alter production data.
|
||||
*
|
||||
*
|
||||
* Some things you might make environment checks for:
|
||||
* - Can I access the database?
|
||||
* - Are the right PHP modules installed?
|
||||
* - Are the file permissions correct?
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
interface EnvironmentCheck
|
||||
{
|
|
@ -1,5 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
use SilverStripe\Core\Extensible;
|
||||
use SilverStripe\Core\Injector\Injectable;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
|
||||
/**
|
||||
* Represents a suite of environment checks.
|
||||
* Specific checks can be registered against a named instance of EnvironmentCheckSuite.
|
||||
|
@ -18,9 +27,14 @@
|
|||
* - mycheck
|
||||
*
|
||||
* $result = EnvironmentCheckSuite::inst('health')->run();
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class EnvironmentCheckSuite extends Object
|
||||
class EnvironmentCheckSuite
|
||||
{
|
||||
use Configurable;
|
||||
use Injectable;
|
||||
use Extensible;
|
||||
/**
|
||||
* Name of this suite.
|
||||
*
|
||||
|
@ -31,7 +45,7 @@ class EnvironmentCheckSuite extends Object
|
|||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $checks = array();
|
||||
protected $checks = [];
|
||||
|
||||
/**
|
||||
* Associative array of named checks registered via the config system. Each check should specify:
|
||||
|
@ -41,7 +55,7 @@ class EnvironmentCheckSuite extends Object
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $registered_checks = array();
|
||||
private static $registered_checks = [];
|
||||
|
||||
/**
|
||||
* Associative array of named suites registered via the config system. Each suite should enumerate
|
||||
|
@ -49,7 +63,7 @@ class EnvironmentCheckSuite extends Object
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $registered_suites = array();
|
||||
private static $registered_suites = [];
|
||||
|
||||
/**
|
||||
* Load checks for this suite from the configuration system. This is an alternative to the
|
||||
|
@ -59,8 +73,6 @@ class EnvironmentCheckSuite extends Object
|
|||
*/
|
||||
public function __construct($suiteName)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
if (empty($this->config()->registered_suites[$suiteName])) {
|
||||
// Not registered via config system, but it still may be configured later via self::register.
|
||||
return;
|
||||
|
@ -77,10 +89,10 @@ class EnvironmentCheckSuite extends Object
|
|||
|
||||
// Existing named checks can be disabled by setting their 'state' to 'disabled'.
|
||||
// This is handy for disabling checks mandated by modules.
|
||||
if (!empty($check['state']) && $check['state']==='disabled') {
|
||||
if (!empty($check['state']) && $check['state'] === 'disabled') {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Add the check to this suite.
|
||||
$this->push($check['definition'], $check['title']);
|
||||
}
|
||||
|
@ -89,7 +101,7 @@ class EnvironmentCheckSuite extends Object
|
|||
/**
|
||||
* Run this test suite and return the result code of the worst result.
|
||||
*
|
||||
* @return int
|
||||
* @return EnvironmentCheckSuiteResult
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
|
@ -113,22 +125,25 @@ class EnvironmentCheckSuite extends Object
|
|||
/**
|
||||
* Get instances of all the environment checks.
|
||||
*
|
||||
* @return array
|
||||
* @return EnvironmentChecker[]
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function checkInstances()
|
||||
{
|
||||
$output = array();
|
||||
$output = [];
|
||||
foreach ($this->checks as $check) {
|
||||
list($checkClass, $checkTitle) = $check;
|
||||
if (is_string($checkClass)) {
|
||||
$checkInst = Object::create_from_string($checkClass);
|
||||
$checkInst = Injector::inst()->create($checkClass);
|
||||
if ($checkInst instanceof EnvironmentCheck) {
|
||||
$output[] = array($checkInst, $checkTitle);
|
||||
$output[] = [$checkInst, $checkTitle];
|
||||
} else {
|
||||
throw new InvalidArgumentException("Bad EnvironmentCheck: '$checkClass' - the named class doesn't implement EnvironmentCheck");
|
||||
throw new InvalidArgumentException(
|
||||
"Bad EnvironmentCheck: '$checkClass' - the named class doesn't implement EnvironmentCheck"
|
||||
);
|
||||
}
|
||||
} elseif ($checkClass instanceof EnvironmentCheck) {
|
||||
$output[] = array($checkClass, $checkTitle);
|
||||
$output[] = [$checkClass, $checkTitle];
|
||||
} else {
|
||||
throw new InvalidArgumentException("Bad EnvironmentCheck: " . var_export($check, true));
|
||||
}
|
||||
|
@ -147,7 +162,7 @@ class EnvironmentCheckSuite extends Object
|
|||
if (!$title) {
|
||||
$title = is_string($check) ? $check : get_class($check);
|
||||
}
|
||||
$this->checks[] = array($check, $title);
|
||||
$this->checks[] = [$check, $title];
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -155,7 +170,7 @@ class EnvironmentCheckSuite extends Object
|
|||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected static $instances = array();
|
||||
protected static $instances = [];
|
||||
|
||||
/**
|
||||
* Return a named instance of EnvironmentCheckSuite.
|
||||
|
@ -182,8 +197,9 @@ class EnvironmentCheckSuite extends Object
|
|||
public static function register($names, $check, $title = null)
|
||||
{
|
||||
if (!is_array($names)) {
|
||||
$names = array($names);
|
||||
$names = [$names];
|
||||
}
|
||||
|
||||
foreach ($names as $name) {
|
||||
self::inst($name)->push($check, $title);
|
||||
}
|
||||
|
@ -194,109 +210,6 @@ class EnvironmentCheckSuite extends Object
|
|||
*/
|
||||
public static function reset()
|
||||
{
|
||||
self::$instances = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A single set of results from running an EnvironmentCheckSuite
|
||||
*/
|
||||
class EnvironmentCheckSuiteResult extends ViewableData
|
||||
{
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
protected $details;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $worst = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->details = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param string $message
|
||||
* @param string $checkIdentifier
|
||||
*/
|
||||
public function addResult($status, $message, $checkIdentifier)
|
||||
{
|
||||
$this->details->push(new ArrayData(array(
|
||||
'Check' => $checkIdentifier,
|
||||
'Status' => $this->statusText($status),
|
||||
'StatusCode' => $status,
|
||||
'Message' => $message,
|
||||
)));
|
||||
|
||||
$this->worst = max($this->worst, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are no errors.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function ShouldPass()
|
||||
{
|
||||
return $this->worst <= EnvironmentCheck::WARNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns overall (i.e. worst) status as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Status()
|
||||
{
|
||||
return $this->statusText($this->worst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns detailed status information about each check.
|
||||
*
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function Details()
|
||||
{
|
||||
return $this->details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the final result status and details to JSON.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJSON()
|
||||
{
|
||||
$result = array(
|
||||
'Status' => $this->Status(),
|
||||
'ShouldPass' => $this->ShouldPass(),
|
||||
'Checks' => array()
|
||||
);
|
||||
foreach ($this->details as $detail) {
|
||||
$result['Checks'][] = $detail->toMap();
|
||||
}
|
||||
return json_encode($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a text version of a status code.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function statusText($status)
|
||||
{
|
||||
switch ($status) {
|
||||
case EnvironmentCheck::ERROR: return "ERROR";
|
||||
case EnvironmentCheck::WARNING: return "WARNING";
|
||||
case EnvironmentCheck::OK: return "OK";
|
||||
case 0: return "NO CHECKS";
|
||||
default: throw new InvalidArgumentException("Bad environment check status '$status'");
|
||||
}
|
||||
self::$instances = [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\ViewableData;
|
||||
|
||||
/**
|
||||
* A single set of results from running an EnvironmentCheckSuite
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class EnvironmentCheckSuiteResult extends ViewableData
|
||||
{
|
||||
/**
|
||||
* @var ArrayList
|
||||
*/
|
||||
protected $details;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $worst = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->details = new ArrayList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $status
|
||||
* @param string $message
|
||||
* @param string $checkIdentifier
|
||||
*/
|
||||
public function addResult($status, $message, $checkIdentifier)
|
||||
{
|
||||
$this->details->push(new ArrayData([
|
||||
'Check' => $checkIdentifier,
|
||||
'Status' => $this->statusText($status),
|
||||
'StatusCode' => $status,
|
||||
'Message' => $message,
|
||||
]));
|
||||
|
||||
$this->worst = max($this->worst, $status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if there are no errors.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function ShouldPass()
|
||||
{
|
||||
return $this->worst <= EnvironmentCheck::WARNING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns overall (i.e. worst) status as a string.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function Status()
|
||||
{
|
||||
return $this->statusText($this->worst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns detailed status information about each check.
|
||||
*
|
||||
* @return ArrayList
|
||||
*/
|
||||
public function Details()
|
||||
{
|
||||
return $this->details;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the final result status and details to JSON.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function toJSON()
|
||||
{
|
||||
$result = [
|
||||
'Status' => $this->Status(),
|
||||
'ShouldPass' => $this->ShouldPass(),
|
||||
'Checks' => []
|
||||
];
|
||||
foreach ($this->details as $detail) {
|
||||
$result['Checks'][] = $detail->toMap();
|
||||
}
|
||||
return json_encode($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a text version of a status code.
|
||||
*
|
||||
* @param int $status
|
||||
* @return string
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
protected function statusText($status)
|
||||
{
|
||||
switch ($status) {
|
||||
case EnvironmentCheck::ERROR:
|
||||
return 'ERROR';
|
||||
break;
|
||||
case EnvironmentCheck::WARNING:
|
||||
return 'WARNING';
|
||||
break;
|
||||
case EnvironmentCheck::OK:
|
||||
return 'OK';
|
||||
break;
|
||||
case 0:
|
||||
return 'NO CHECKS';
|
||||
break;
|
||||
default:
|
||||
throw new InvalidArgumentException("Bad environment check status '$status'");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,337 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck;
|
||||
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\Email\Email;
|
||||
use SilverStripe\Control\HTTPResponse;
|
||||
use SilverStripe\Control\HTTPResponse_Exception;
|
||||
use SilverStripe\Control\RequestHandler;
|
||||
use SilverStripe\Core\Environment;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\Security;
|
||||
|
||||
/**
|
||||
* Provides an interface for checking the given EnvironmentCheckSuite.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class EnvironmentChecker extends RequestHandler
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $url_handlers = [
|
||||
'' => 'index',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $checkSuiteName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $includeDetails = false;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $errorCode = 500;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private static $to_email_address = null;
|
||||
|
||||
/**
|
||||
* @var null|string
|
||||
*/
|
||||
private static $from_email_address = null;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $email_results = false;
|
||||
|
||||
/**
|
||||
* @var bool Log results via {@link \Psr\Log\LoggerInterface}
|
||||
*/
|
||||
private static $log_results_warning = false;
|
||||
|
||||
/**
|
||||
* @var string Maps to {@link \Psr\Log\LogLevel} levels. Defaults to LogLevel::WARNING
|
||||
*/
|
||||
private static $log_results_warning_level = LogLevel::WARNING;
|
||||
|
||||
/**
|
||||
* @var bool Log results via a {@link \Psr\Log\LoggerInterface}
|
||||
*/
|
||||
private static $log_results_error = false;
|
||||
|
||||
/**
|
||||
* @var int Maps to {@link \Psr\Log\LogLevel} levels. Defaults to LogLevel::ALERT
|
||||
*/
|
||||
private static $log_results_error_level = LogLevel::ALERT;
|
||||
|
||||
/**
|
||||
* @param string $checkSuiteName
|
||||
* @param string $title
|
||||
*/
|
||||
public function __construct($checkSuiteName, $title)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->checkSuiteName = $checkSuiteName;
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $permission
|
||||
*
|
||||
* @throws HTTPResponse_Exception
|
||||
*/
|
||||
public function init($permission = 'ADMIN')
|
||||
{
|
||||
// if the environment supports it, provide a basic auth challenge and see if it matches configured credentials
|
||||
if (Environment::getEnv('ENVCHECK_BASICAUTH_USERNAME')
|
||||
&& Environment::getEnv('ENVCHECK_BASICAUTH_PASSWORD')
|
||||
) {
|
||||
// Check that details are both provided, and match
|
||||
if (empty($_SERVER['PHP_AUTH_USER']) || empty($_SERVER['PHP_AUTH_PW'])
|
||||
|| $_SERVER['PHP_AUTH_USER'] != Environment::getEnv('ENVCHECK_BASICAUTH_USERNAME')
|
||||
|| $_SERVER['PHP_AUTH_PW'] != Environment::getEnv('ENVCHECK_BASICAUTH_PASSWORD')
|
||||
) {
|
||||
// Fail check with basic auth challenge
|
||||
$response = new HTTPResponse(null, 401);
|
||||
$response->addHeader('WWW-Authenticate', "Basic realm=\"Environment check\"");
|
||||
throw new HTTPResponse_Exception($response);
|
||||
}
|
||||
} elseif (!$this->canAccess(null, $permission)) {
|
||||
// Fail check with silverstripe login challenge
|
||||
$result = Security::permissionFailure(
|
||||
$this,
|
||||
"You must have the {$permission} permission to access this check"
|
||||
);
|
||||
throw new HTTPResponse_Exception($result);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current member can access the environment checker
|
||||
*
|
||||
* @param null|int|Member $member
|
||||
* @param string $permission
|
||||
* @return bool
|
||||
*/
|
||||
public function canAccess($member = null, $permission = 'ADMIN')
|
||||
{
|
||||
if (!$member) {
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
|
||||
// We allow access to this controller regardless of live-status or ADMIN permission only
|
||||
// if on CLI. Access to this controller is always allowed in "dev-mode", or of the user is ADMIN.
|
||||
if (Director::isDev()
|
||||
|| Director::is_cli()
|
||||
|| empty($permission)
|
||||
|| Permission::checkMember($member, $permission)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extended access checks.
|
||||
// "Veto" style, return NULL to abstain vote.
|
||||
$canExtended = null;
|
||||
$results = $this->extend('canAccess', $member);
|
||||
if ($results && is_array($results)) {
|
||||
if (!min($results)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return HTTPResponse
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$response = new HTTPResponse;
|
||||
$result = EnvironmentCheckSuite::inst($this->checkSuiteName)->run();
|
||||
|
||||
if (!$result->ShouldPass()) {
|
||||
$response->setStatusCode($this->errorCode);
|
||||
}
|
||||
|
||||
$data = [
|
||||
'URL' => Director::absoluteBaseURL(),
|
||||
'Title' => $this->title,
|
||||
'Name' => $this->checkSuiteName,
|
||||
'ErrorCode' => $this->errorCode
|
||||
];
|
||||
|
||||
$emailContent = $result->customise(array_merge($data, [
|
||||
'IncludeDetails' => true
|
||||
]))->renderWith(__CLASS__);
|
||||
|
||||
if (!$this->includeDetails) {
|
||||
$webContent = $result->customise(array_merge($data, [
|
||||
'IncludeDetails' => false
|
||||
]))->renderWith(__CLASS__);
|
||||
} else {
|
||||
$webContent = $emailContent;
|
||||
}
|
||||
|
||||
if ($this->config()->get('email_results') && !$result->ShouldPass()) {
|
||||
$email = new Email(
|
||||
$this->config()->get('from_email_address'),
|
||||
$this->config()->get('to_email_address'),
|
||||
$this->title,
|
||||
$emailContent
|
||||
);
|
||||
$email->send();
|
||||
}
|
||||
|
||||
// Optionally log errors and warnings individually
|
||||
foreach ($result->Details() as $detail) {
|
||||
if ($this->config()->get('log_results_warning') && $detail->StatusCode == EnvironmentCheck::WARNING) {
|
||||
$this->log(
|
||||
sprintf('EnvironmentChecker warning at "%s" check. Message: %s', $detail->Check, $detail->Message),
|
||||
$this->config()->get('log_results_warning_level')
|
||||
);
|
||||
} elseif ($this->config()->get('log_results_error') && $detail->StatusCode == EnvironmentCheck::ERROR) {
|
||||
$this->log(
|
||||
sprintf('EnvironmentChecker error at "%s" check. Message: %s', $detail->Check, $detail->Message),
|
||||
$this->config()->get('log_results_error_level')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// output the result as JSON if requested
|
||||
if ($this->getRequest()->getExtension() == 'json'
|
||||
|| strpos($this->getRequest()->getHeader('Accept') ?? '', 'application/json') !== false
|
||||
) {
|
||||
$response->setBody($result->toJSON());
|
||||
$response->addHeader('Content-Type', 'application/json');
|
||||
return $response;
|
||||
}
|
||||
|
||||
$response->setBody($webContent);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a log entry to the configured PSR-3 LoggerInterface
|
||||
*
|
||||
* @param string $message
|
||||
* @param int $level
|
||||
*/
|
||||
public function log($message, $level)
|
||||
{
|
||||
Injector::inst()->get(LoggerInterface::class)->log($level, $message);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the HTTP status code that should be returned when there's an error.
|
||||
*
|
||||
* @param int $errorCode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setErrorCode($errorCode)
|
||||
{
|
||||
$this->errorCode = $errorCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to include the full breakdown of services
|
||||
*
|
||||
* @param bool $includeDetails
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setIncludeDetails($includeDetails)
|
||||
{
|
||||
$this->includeDetails = $includeDetails;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.0.0 Use config API instead
|
||||
* @param string $from
|
||||
*/
|
||||
public static function set_from_email_address($from)
|
||||
{
|
||||
Deprecation::notice('2.0.0', 'Use config API instead');
|
||||
static::config()->set('from_email_address', $from);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.0.0 Use config API instead
|
||||
* @return null|string
|
||||
*/
|
||||
public static function get_from_email_address()
|
||||
{
|
||||
Deprecation::notice('2.0.0', 'Use config API instead');
|
||||
return static::config()->get('from_email_address');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.0.0 Use config API instead
|
||||
* @param string $to
|
||||
*/
|
||||
public static function set_to_email_address($to)
|
||||
{
|
||||
Deprecation::notice('2.0.0', 'Use config API instead');
|
||||
static::config()->set('to_email_address', $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.0.0 Use config API instead
|
||||
* @return null|string
|
||||
*/
|
||||
public static function get_to_email_address()
|
||||
{
|
||||
Deprecation::notice('2.0.0', 'Use config API instead');
|
||||
return static::config()->get('to_email_address');
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.0.0 Use config API instead
|
||||
* @param bool $results
|
||||
*/
|
||||
public static function set_email_results($results)
|
||||
{
|
||||
Deprecation::notice('2.0.0', 'Use config API instead');
|
||||
static::config()->set('email_results', $results);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated 2.0.0 Use config API instead
|
||||
* @return bool
|
||||
*/
|
||||
public static function get_email_results()
|
||||
{
|
||||
Deprecation::notice('2.0.0', 'Use config API instead');
|
||||
return static::config()->get('email_results');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Services;
|
||||
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use SilverStripe\Core\Injector\Factory;
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
|
||||
/**
|
||||
* Factory class for creating HTTP client which are injected into some env check classes. Inject via YAML,
|
||||
* arguments for Guzzle client can be supplied using "constructor" property or set as default_config.
|
||||
*
|
||||
* @see SilverStripe\EnvironmentCheck\Traits\Fetcher
|
||||
*/
|
||||
class ClientFactory implements Factory
|
||||
{
|
||||
use Configurable;
|
||||
|
||||
/**
|
||||
* Default config for Guzzle client.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $default_config = [];
|
||||
|
||||
/**
|
||||
* Wrapper to create a Guzzle client.
|
||||
*
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function create($service, array $params = [])
|
||||
{
|
||||
return new GuzzleClient($this->getConfig($params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge config provided from yaml with default config
|
||||
*
|
||||
* @param array $overrides
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig(array $overrides)
|
||||
{
|
||||
return array_merge(
|
||||
$this->config()->get('default_config'),
|
||||
$overrides
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Traits;
|
||||
|
||||
use SilverStripe\Control\Director;
|
||||
|
||||
/**
|
||||
* Simple helper for env checks which require HTTP clients.
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
trait Fetcher
|
||||
{
|
||||
/**
|
||||
* Client for making requests, set vi Injector.
|
||||
*
|
||||
* @see SilverStripe\EnvironmentCheck\Services
|
||||
*
|
||||
* @var GuzzleHttp\Client
|
||||
*/
|
||||
public $client = null;
|
||||
|
||||
/**
|
||||
* Absolute URL for requests.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $url;
|
||||
|
||||
/**
|
||||
* Set URL for requests.
|
||||
*
|
||||
* @param string $url Relative URL
|
||||
* @return self
|
||||
*/
|
||||
public function setURL($url)
|
||||
{
|
||||
$this->url = Director::absoluteURL($url);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for URL
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getURL()
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@
|
|||
margin-top: -10px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
font-size: 30px;
|
||||
margin-bottom: 3px;
|
||||
|
@ -21,11 +21,11 @@
|
|||
font-size: 16px;
|
||||
margin: 2px 0 10px 8px;
|
||||
}
|
||||
|
||||
|
||||
p {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
@ -44,7 +44,7 @@
|
|||
table tr:nth-child(odd) td {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
|
||||
.OK {
|
||||
color: green;
|
||||
}
|
||||
|
@ -59,23 +59,25 @@
|
|||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
|
||||
<body>
|
||||
<h1 class="$Status">$Title: $Status</h1>
|
||||
<h2 class="website">Site: $URL</h2>
|
||||
|
||||
|
||||
<% if $IncludeDetails %>
|
||||
<table>
|
||||
<tr><th>Check</th> <th>Status</th> <th>Message</th></tr>
|
||||
<% loop Details %>
|
||||
<% loop $Details %>
|
||||
<tr><td>$Check</td> <td class="$Status">$Status</td> <td>$Message.XML</td></tr>
|
||||
<% end_loop %>
|
||||
</table>
|
||||
<% end_if %>
|
||||
|
||||
<% if ShouldPass %>
|
||||
<p>Site is available</p>
|
||||
<p class="subtext">(you may check for the presence of the text 'Site is available' rather than an HTTP $ErrorCode error on this page, if you prefer.)</p>
|
||||
<% if $ShouldPass %>
|
||||
<p>Site is available</p>
|
||||
<p class="subtext">(you may check for the presence of the text 'Site is available' rather than an HTTP $ErrorCode error on this page, if you prefer.<% if not $IncludeDetails %> Full details are available for logged in users at <a href="{$AbsoluteBaseURL}dev/check/">dev/check</a><% end_if %>)</p>
|
||||
<% else %>
|
||||
<% if Name == check %>
|
||||
<% if $Name == "check" %>
|
||||
<p><b>A subsystem of the site is unavailable, but the site remains operational</b></p>
|
||||
<% else %>
|
||||
<p><b>Site is not available</b></p>
|
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\EnvironmentCheck\Checks\CacheHeadersCheck;
|
||||
|
||||
/**
|
||||
* Test session checks.
|
||||
*/
|
||||
class CacheHeadersCheckTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* Test that directives that must be included, are.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMustInclude()
|
||||
{
|
||||
// Create a mock and queue responses
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Cache-Control' => 'must-revalidate', 'ETag' => '123']),
|
||||
new Response(200, ['Cache-Control' =>'no-cache', 'ETag' => '123']),
|
||||
new Response(200, ['ETag' => '123']),
|
||||
new Response(200, ['Cache-Control' => 'must-revalidate, private', 'ETag' => '123']),
|
||||
]);
|
||||
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$cacheHeadersCheck = new CacheHeadersCheck('/', ['must-revalidate']);
|
||||
$cacheHeadersCheck->client = $client;
|
||||
|
||||
// Run checks for each response above
|
||||
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that directives that must be excluded, are.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testMustExclude()
|
||||
{
|
||||
// Create a mock and queue responses
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Cache-Control' => 'must-revalidate', 'ETag' => '123']),
|
||||
new Response(200, ['Cache-Control' =>'no-cache', 'ETag' => '123']),
|
||||
new Response(200, ['ETag' => '123']),
|
||||
new Response(200, ['Cache-Control' =>'private, no-store', 'ETag' => '123']),
|
||||
]);
|
||||
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$cacheHeadersCheck = new CacheHeadersCheck('/', [], ["no-store", "no-cache", "private"]);
|
||||
$cacheHeadersCheck->client = $client;
|
||||
|
||||
// Run checks for each response above
|
||||
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that Etag header must exist in response.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEtag()
|
||||
{
|
||||
// Create a mock and queue responses
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Cache-Control' => 'must-revalidate', 'ETag' => '123']),
|
||||
new Response(200, ['Cache-Control' =>'no-cache']),
|
||||
new Response(200, ['ETag' => '123']),
|
||||
new Response(200, []),
|
||||
]);
|
||||
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
|
||||
$cacheHeadersCheck = new CacheHeadersCheck('/');
|
||||
$cacheHeadersCheck->client = $client;
|
||||
|
||||
// Run checks for each response above
|
||||
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::OK, $cacheHeadersCheck->check());
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $cacheHeadersCheck->check());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\EnvironmentCheck\Checks\DatabaseCheck;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
/**
|
||||
* Class DatabaseCheckTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class DatabaseCheckTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var bool
|
||||
*/
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function testCheckReportsValidConnection()
|
||||
{
|
||||
$member = new Member;
|
||||
$member->FirstName = 'Bob';
|
||||
$member->write();
|
||||
|
||||
$check = new DatabaseCheck();
|
||||
|
||||
$expected = [
|
||||
EnvironmentCheck::OK,
|
||||
''
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use SilverStripe\Core\Kernel;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\EnvironmentCheck\Checks\EnvTypeCheck;
|
||||
|
||||
/**
|
||||
* Test the env setting check.
|
||||
*/
|
||||
class EnvTypeCheckTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* Check is OK when in live mode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEnvSettingLive()
|
||||
{
|
||||
/** @var Kernel $kernel */
|
||||
$kernel = Injector::inst()->get(Kernel::class);
|
||||
$kernel->setEnvironment('live');
|
||||
|
||||
$this->assertTrue(Director::isLive());
|
||||
|
||||
$checker = Injector::inst()->get(EnvTypeCheck::class);
|
||||
$result = $checker->check();
|
||||
|
||||
$this->assertSame($result[0], EnvironmentCheck::OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is ERROR when in test mode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEnvSettingTest()
|
||||
{
|
||||
/** @var Kernel $kernel */
|
||||
$kernel = Injector::inst()->get(Kernel::class);
|
||||
$kernel->setEnvironment('test');
|
||||
|
||||
$this->assertTrue(Director::isTest());
|
||||
|
||||
$checker = Injector::inst()->get(EnvTypeCheck::class);
|
||||
$result = $checker->check();
|
||||
|
||||
$this->assertSame($result[0], EnvironmentCheck::ERROR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is ERROR when in dev mode
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testEnvSettingDev()
|
||||
{
|
||||
/** @var Kernel $kernel */
|
||||
$kernel = Injector::inst()->get(Kernel::class);
|
||||
$kernel->setEnvironment('dev');
|
||||
|
||||
$this->assertTrue(Director::isDev());
|
||||
|
||||
$checker = Injector::inst()->get(EnvTypeCheck::class);
|
||||
$result = $checker->check();
|
||||
|
||||
$this->assertSame($result[0], EnvironmentCheck::ERROR);
|
||||
}
|
||||
}
|
|
@ -1,7 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\EnvironmentCheck\Checks\ExternalURLCheck;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
* Class ExternalURLCheckTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class ExternalURLCheckTest extends SapphireTest
|
||||
{
|
||||
|
@ -11,10 +19,10 @@ class ExternalURLCheckTest extends SapphireTest
|
|||
|
||||
$check = new ExternalURLCheck('http://missing-site/');
|
||||
|
||||
$expected = array(
|
||||
$expected = [
|
||||
EnvironmentCheck::ERROR,
|
||||
'Success retrieving "http://missing-site/" (Code: 404)',
|
||||
);
|
||||
'Success retrieving "http://missing-site/" (Code: 404)'
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
|
@ -1,7 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\Checks\FileWriteableCheck;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
* Class FileWritableCheckTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class FileWritableCheckTest extends SapphireTest
|
||||
{
|
||||
|
@ -9,10 +17,10 @@ class FileWritableCheckTest extends SapphireTest
|
|||
{
|
||||
$check = new FileWriteableCheck(TEMP_FOLDER);
|
||||
|
||||
$expected = array(
|
||||
$expected = [
|
||||
EnvironmentCheck::OK,
|
||||
'',
|
||||
);
|
||||
''
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
|
@ -1,7 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\EnvironmentCheck\Checks\HasClassCheck;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
* Class HasClassCheckTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class HasClassCheckTest extends SapphireTest
|
||||
{
|
||||
|
@ -9,10 +17,10 @@ class HasClassCheckTest extends SapphireTest
|
|||
{
|
||||
$check = new HasClassCheck('foo');
|
||||
|
||||
$expected = array(
|
||||
$expected = [
|
||||
EnvironmentCheck::ERROR,
|
||||
'Class foo doesn\'t exist',
|
||||
);
|
||||
'Class foo doesn\'t exist'
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
||||
|
@ -21,10 +29,10 @@ class HasClassCheckTest extends SapphireTest
|
|||
{
|
||||
$check = new HasClassCheck('stdClass');
|
||||
|
||||
$expected = array(
|
||||
EnvironmentCheck::OK,
|
||||
'Class stdClass exists',
|
||||
);
|
||||
$expected = [
|
||||
EnvironmentCheck::OK,
|
||||
'Class stdClass exists',
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
|
@ -1,7 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\Checks\HasFunctionCheck;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
* Class HasFunctionCheckTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class HasFunctionCheckTest extends SapphireTest
|
||||
{
|
||||
|
@ -9,10 +17,10 @@ class HasFunctionCheckTest extends SapphireTest
|
|||
{
|
||||
$check = new HasFunctionCheck('foo');
|
||||
|
||||
$expected = array(
|
||||
$expected = [
|
||||
EnvironmentCheck::ERROR,
|
||||
'foo() doesn\'t exist',
|
||||
);
|
||||
'foo() doesn\'t exist'
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
||||
|
@ -21,10 +29,10 @@ class HasFunctionCheckTest extends SapphireTest
|
|||
{
|
||||
$check = new HasFunctionCheck('class_exists');
|
||||
|
||||
$expected = array(
|
||||
$expected = [
|
||||
EnvironmentCheck::OK,
|
||||
'class_exists() exists',
|
||||
);
|
||||
'class_exists() exists'
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use GuzzleHttp\Handler\MockHandler;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\EnvironmentCheck\Checks\SessionCheck;
|
||||
|
||||
/**
|
||||
* Test session checks.
|
||||
*/
|
||||
class SessionCheckTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* @var SilverStripe\EnvironmentCheck\Checks\SessionCheck
|
||||
*/
|
||||
public $sessionCheck = null;
|
||||
|
||||
/**
|
||||
* Create a session check for use by tests.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->sessionCheck = new SessionCheck('/');
|
||||
}
|
||||
|
||||
/**
|
||||
* Env check reports error when session cookies are being set.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSessionSet()
|
||||
{
|
||||
// Create a mock and queue two responses.
|
||||
$mock = new MockHandler([
|
||||
new Response(200, ['Set-Cookie' => 'PHPSESSID:foo']),
|
||||
new Response(200, ['Set-Cookie' => 'SECSESSID:bar'])
|
||||
]);
|
||||
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->sessionCheck->client = $client;
|
||||
|
||||
// Check for PHPSESSID
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $this->sessionCheck->check());
|
||||
|
||||
// Check for SECSESSID
|
||||
$this->assertContains(EnvironmentCheck::ERROR, $this->sessionCheck->check());
|
||||
}
|
||||
|
||||
/**
|
||||
* Env check responds OK when no session cookies are set in response.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testSessionNotSet()
|
||||
{
|
||||
// Create a mock and queue two responses.
|
||||
$mock = new MockHandler([
|
||||
new Response(200)
|
||||
]);
|
||||
|
||||
$handler = HandlerStack::create($mock);
|
||||
$client = new Client(['handler' => $handler]);
|
||||
$this->sessionCheck->client = $client;
|
||||
|
||||
$this->assertContains(EnvironmentCheck::OK, $this->sessionCheck->check());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Checks;
|
||||
|
||||
use SilverStripe\EnvironmentCheck\Checks\URLCheck;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
|
||||
/**
|
||||
* Class URLCheckTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class URLCheckTest extends SapphireTest
|
||||
{
|
||||
public function testCheckReportsMissingPages()
|
||||
{
|
||||
$check = new URLCheck('foo', 'bar');
|
||||
|
||||
$expected = [
|
||||
EnvironmentCheck::ERROR,
|
||||
'Error retrieving "foo" (Code: 404)'
|
||||
];
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Controllers;
|
||||
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\EnvironmentCheck\Controllers\DevCheckController;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentChecker;
|
||||
|
||||
/**
|
||||
* Class DevCheckControllerTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class DevCheckControllerTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var array
|
||||
*/
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function testIndexCreatesChecker()
|
||||
{
|
||||
$controller = new DevCheckController();
|
||||
|
||||
$request = new HTTPRequest('GET', 'example.com');
|
||||
|
||||
$this->assertInstanceOf(EnvironmentChecker::class, $controller->index($request));
|
||||
}
|
||||
|
||||
public function testCheckIncludesDetails()
|
||||
{
|
||||
$controller = new DevCheckController();
|
||||
$request = new HTTPRequest('GET', 'example.com');
|
||||
|
||||
$response = $controller->index($request)->index();
|
||||
|
||||
$this->assertStringContainsString('<table>', $response->getBody());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\Controllers;
|
||||
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\EnvironmentCheck\Controllers\DevHealthController;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentChecker;
|
||||
|
||||
/**
|
||||
* Class DevHealthControllerTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class DevHealthControllerTest extends SapphireTest
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @var array
|
||||
*/
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function testIndexCreatesChecker()
|
||||
{
|
||||
$controller = new DevHealthController();
|
||||
|
||||
$request = new HTTPRequest('GET', 'example.com');
|
||||
|
||||
// we need to fake authenticated access as BasicAuth::requireLogin doesn't like empty
|
||||
// permission type strings, which is what health check uses.
|
||||
|
||||
putenv('ENVCHECK_BASICAUTH_USERNAME="foo"');
|
||||
putenv('ENVCHECK_BASICAUTH_PASSWORD="bar"');
|
||||
|
||||
$_SERVER['PHP_AUTH_USER'] = 'foo';
|
||||
$_SERVER['PHP_AUTH_PW'] = 'bar';
|
||||
|
||||
$this->assertInstanceOf(EnvironmentChecker::class, $controller->index($request));
|
||||
}
|
||||
|
||||
|
||||
public function testHealthDoesNotIncludeDetails()
|
||||
{
|
||||
$controller = new DevHealthController();
|
||||
$request = new HTTPRequest('GET', 'example.com');
|
||||
|
||||
$response = $controller->index($request)->index();
|
||||
|
||||
$this->assertFalse(strpos($response->getBody(), '<table>'));
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
class DevCheckControllerTest extends SapphireTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function testIndexCreatesChecker()
|
||||
{
|
||||
$controller = new DevCheckController();
|
||||
|
||||
$request = new SS_HTTPRequest('GET', 'example.com');
|
||||
|
||||
$this->assertInstanceOf('EnvironmentChecker', $controller->index($request));
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
class DevHealthControllerTest extends SapphireTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function testIndexCreatesChecker()
|
||||
{
|
||||
$controller = new DevHealthController();
|
||||
|
||||
$request = new SS_HTTPRequest('GET', 'example.com');
|
||||
|
||||
// we need to fake authenticated access as BasicAuth::requireLogin doesn't like empty
|
||||
// permission type strings, which is what health check uses.
|
||||
|
||||
define('ENVCHECK_BASICAUTH_USERNAME', 'foo');
|
||||
define('ENVCHECK_BASICAUTH_PASSWORD', 'bar');
|
||||
|
||||
$_SERVER['PHP_AUTH_USER'] = 'foo';
|
||||
$_SERVER['PHP_AUTH_PW'] = 'bar';
|
||||
|
||||
$this->assertInstanceOf('EnvironmentChecker', $controller->index($request));
|
||||
}
|
||||
}
|
|
@ -1,102 +1,97 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests;
|
||||
|
||||
use Monolog\Logger;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\LogLevel;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentChecker;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheckSuite;
|
||||
|
||||
/**
|
||||
* Class EnvironmentCheckerTest
|
||||
*
|
||||
* @package environmentcheck
|
||||
*/
|
||||
class EnvironmentCheckerTest extends SapphireTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function setUpOnce()
|
||||
protected function tearDown(): void
|
||||
{
|
||||
parent::setUpOnce();
|
||||
|
||||
Phockito::include_hamcrest();
|
||||
}
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Config::nest();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
{
|
||||
Config::unnest();
|
||||
|
||||
EnvironmentCheckSuite::reset();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testOnlyLogsWithErrors()
|
||||
{
|
||||
Config::inst()->update('EnvironmentChecker', 'log_results_warning', true);
|
||||
Config::inst()->update('EnvironmentChecker', 'log_results_error', true);
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest_CheckNoErrors());
|
||||
$checker = Phockito::spy(
|
||||
'EnvironmentChecker',
|
||||
'test suite',
|
||||
'test'
|
||||
);
|
||||
Config::modify()->set(EnvironmentChecker::class, 'log_results_warning', true);
|
||||
Config::modify()->set(EnvironmentChecker::class, 'log_results_error', true);
|
||||
|
||||
$response = $checker->index();
|
||||
Phockito::verify($checker, 0)->log(anything(), anything());
|
||||
EnvironmentCheckSuite::reset();
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest\CheckNoErrors());
|
||||
|
||||
$logger = $this->getMockBuilder(Logger::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['log'])
|
||||
->getMock();
|
||||
|
||||
$logger->expects($this->never())->method('log');
|
||||
|
||||
Injector::inst()->registerService($logger, LoggerInterface::class);
|
||||
|
||||
(new EnvironmentChecker('test suite', 'test'))->index();
|
||||
}
|
||||
|
||||
public function testLogsWithWarnings()
|
||||
{
|
||||
Config::inst()->update('EnvironmentChecker', 'log_results_warning', true);
|
||||
Config::inst()->update('EnvironmentChecker', 'log_results_error', false);
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest_CheckWarnings());
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest_CheckErrors());
|
||||
$checker = Phockito::spy(
|
||||
'EnvironmentChecker',
|
||||
'test suite',
|
||||
'test'
|
||||
);
|
||||
Config::modify()->set(EnvironmentChecker::class, 'log_results_warning', true);
|
||||
Config::modify()->set(EnvironmentChecker::class, 'log_results_error', false);
|
||||
|
||||
$response = $checker->index();
|
||||
Phockito::verify($checker, 1)->log(containsString('warning'), anything());
|
||||
Phockito::verify($checker, 0)->log(containsString('error'), anything());
|
||||
EnvironmentCheckSuite::reset();
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest\CheckWarnings());
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest\CheckErrors());
|
||||
|
||||
$logger = $this->getMockBuilder(Logger::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['log'])
|
||||
->getMock();
|
||||
|
||||
$logger->expects($this->once())
|
||||
->method('log')
|
||||
->withConsecutive(
|
||||
[$this->equalTo(LogLevel::WARNING)],
|
||||
[$this->anything()]
|
||||
);
|
||||
|
||||
Injector::inst()->registerService($logger, LoggerInterface::class);
|
||||
|
||||
(new EnvironmentChecker('test suite', 'test'))->index();
|
||||
}
|
||||
|
||||
public function testLogsWithErrors()
|
||||
{
|
||||
Config::inst()->update('EnvironmentChecker', 'log_results_error', false);
|
||||
Config::inst()->update('EnvironmentChecker', 'log_results_error', true);
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest_CheckWarnings());
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest_CheckErrors());
|
||||
$checker = Phockito::spy(
|
||||
'EnvironmentChecker',
|
||||
'test suite',
|
||||
'test'
|
||||
);
|
||||
Config::modify()->set(EnvironmentChecker::class, 'log_results_error', false);
|
||||
Config::modify()->set(EnvironmentChecker::class, 'log_results_error', true);
|
||||
|
||||
$response = $checker->index();
|
||||
Phockito::verify($checker, 0)->log(containsString('warning'), anything());
|
||||
Phockito::verify($checker, 1)->log(containsString('error'), anything());
|
||||
EnvironmentCheckSuite::reset();
|
||||
}
|
||||
}
|
||||
|
||||
class EnvironmentCheckerTest_CheckNoErrors implements EnvironmentCheck, TestOnly
|
||||
{
|
||||
public function check()
|
||||
{
|
||||
return array(EnvironmentCheck::OK, '');
|
||||
}
|
||||
}
|
||||
|
||||
class EnvironmentCheckerTest_CheckWarnings implements EnvironmentCheck, TestOnly
|
||||
{
|
||||
public function check()
|
||||
{
|
||||
return array(EnvironmentCheck::WARNING, "test warning");
|
||||
}
|
||||
}
|
||||
|
||||
class EnvironmentCheckerTest_CheckErrors implements EnvironmentCheck, TestOnly
|
||||
{
|
||||
public function check()
|
||||
{
|
||||
return array(EnvironmentCheck::ERROR, "test error");
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest\CheckWarnings());
|
||||
EnvironmentCheckSuite::register('test suite', new EnvironmentCheckerTest\CheckErrors());
|
||||
|
||||
$logger = $this->getMockBuilder(Logger::class)
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['log'])
|
||||
->getMock();
|
||||
|
||||
$logger->expects($this->once())
|
||||
->method('log')
|
||||
->withConsecutive(
|
||||
[$this->equalTo(LogLevel::ALERT), $this->anything()],
|
||||
[$this->equalTo(LogLevel::WARNING), $this->anything()]
|
||||
);
|
||||
|
||||
Injector::inst()->registerService($logger, LoggerInterface::class);
|
||||
|
||||
(new EnvironmentChecker('test suite', 'test'))->index();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\EnvironmentCheckerTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
class CheckErrors implements EnvironmentCheck, TestOnly
|
||||
{
|
||||
public function check()
|
||||
{
|
||||
return [EnvironmentCheck::ERROR, 'test error'];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\EnvironmentCheckerTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
class CheckNoErrors implements EnvironmentCheck, TestOnly
|
||||
{
|
||||
public function check()
|
||||
{
|
||||
return [EnvironmentCheck::OK, ''];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace SilverStripe\EnvironmentCheck\Tests\EnvironmentCheckerTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\EnvironmentCheck\EnvironmentCheck;
|
||||
|
||||
class CheckWarnings implements EnvironmentCheck, TestOnly
|
||||
{
|
||||
public function check()
|
||||
{
|
||||
return [EnvironmentCheck::WARNING, 'test warning'];
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
class DatabaseCheckTest extends SapphireTest
|
||||
{
|
||||
protected $usesDatabase = true;
|
||||
|
||||
public function testCheckReportsValidConnection()
|
||||
{
|
||||
$check = new DatabaseCheck();
|
||||
|
||||
$expected = array(
|
||||
EnvironmentCheck::OK,
|
||||
'',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @mixin PHPUnit_Framework_TestCase
|
||||
*/
|
||||
class URLCheckTest extends SapphireTest
|
||||
{
|
||||
public function testCheckReportsMissingPages()
|
||||
{
|
||||
$check = new URLCheck('foo', 'bar');
|
||||
|
||||
$expected = array(
|
||||
EnvironmentCheck::ERROR,
|
||||
'Error retrieving "foo" (Code: 404)',
|
||||
);
|
||||
|
||||
$this->assertEquals($expected, $check->check());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue