diff --git a/README.md b/README.md index 5633533..cf8eaba 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,11 @@ In general a metric is any real time value that you might push to another servic ## Branches -| Moodle version | Branch | PHP | -|-------------------|------------------|------| -| Moodle 3.5+ | MOODLE_35_STABLE | 7.1+ | -| Totara 10+ | MOODLE_35_STABLE | 7.1+ | +| Moodle version | Branch | PHP | +|------------------|-------------------|------| +| Moodle 3.5 - 4.3 | MOODLE_35_STABLE | 7.1+ | +| Moodle 4.4+ | MOODLE_404_STABLE | 8.1+ | +| Totara 10+ | MOODLE_35_STABLE | 7.1+ | ## Architecture @@ -106,7 +107,6 @@ Sends metric data to AWS Cloudwatch https://docs.aws.amazon.com/cloudwatch/ #### Requires -- local/aws AWS SDK plugin (https://github.com/catalyst/moodle-local_aws) - AWS account - IAM user diff --git a/classes/admin_settings_aws_region.php b/classes/admin_settings_aws_region.php new file mode 100644 index 0000000..ab8ef3e --- /dev/null +++ b/classes/admin_settings_aws_region.php @@ -0,0 +1,87 @@ +. + +/** + * Admin setting for AWS regions. + * + * @package tool_cloudmetrics + * @author Dmitrii Metelkin + * @copyright 2020 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_cloudmetrics; + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/lib/adminlib.php'); + +/** + * Admin setting for a list of AWS regions. + * + * @package tool_cloudmetrics + * @copyright 2020 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class admin_settings_aws_region extends \admin_setting_configtext { + + /** + * Return part of form with setting. + * + * @param mixed $data array or string depending on setting + * @param string $query + * @return string + */ + public function output_html($data, $query='') { + global $CFG; + + $default = $this->get_defaultsetting(); + + $options = []; + + $all = require($CFG->dirroot . '/lib/aws-sdk/src/data/endpoints.json.php'); + $ends = $all['partitions'][0]['regions']; + if ($ends) { + foreach ($ends as $key => $value) { + $options[] = [ + 'value' => $key, + 'label' => $key . ' - ' . $value['description'], + ]; + } + } + + $inputparams = array( + 'type' => 'text', + 'list' => $this->get_full_name(), + 'name' => $this->get_full_name(), + 'value' => $data, + 'size' => $this->size, + 'id' => $this->get_id(), + 'class' => 'form-control text-ltr', + ); + + $element = \html_writer::start_tag('div', array('class' => 'form-text defaultsnext')); + $element .= \html_writer::empty_tag('input', $inputparams); + $element .= \html_writer::start_tag('datalist', array('id' => $this->get_full_name())); + foreach ($options as $option) { + $element .= \html_writer::tag('option', $option['label'], array('value' => $option['value'])); + } + $element .= \html_writer::end_tag('datalist'); + $element .= \html_writer::end_tag('div'); + + return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query); + } +} diff --git a/classes/aws_helper.php b/classes/aws_helper.php new file mode 100644 index 0000000..fc83f1e --- /dev/null +++ b/classes/aws_helper.php @@ -0,0 +1,100 @@ +. + +/** + * AWS helper class. Contains useful functions when interacting with the SDK. + * + * @package tool_cloudmetrics + * @author Peter Burnett + * @copyright 2020 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_cloudmetrics; + +use Aws\CommandInterface; +use Aws\AwsClient; +use Psr\Http\Message\RequestInterface; + +/** + * This class contains functions that help plugins to interact with the AWS SDK. + * + * @copyright 2020 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class aws_helper { + + /** + * This creates a proxy string suitable for use with the AWS SDK. + * + * @return string the string to use for proxy settings. + */ + public static function get_proxy_string() { + global $CFG; + $proxy = ''; + if (empty($CFG->proxytype)) { + return $proxy; + } + if ($CFG->proxytype === 'SOCKS5') { + // If it is a SOCKS proxy, append the protocol info. + $protocol = 'socks5://'; + } else { + $protocol = ''; + } + if (!empty($CFG->proxyhost)) { + $proxy = $CFG->proxyhost; + if (!empty($CFG->proxyport)) { + $proxy .= ':'. $CFG->proxyport; + } + if (!empty($CFG->proxyuser) && !empty($CFG->proxypassword)) { + $proxy = $protocol . $CFG->proxyuser . ':' . $CFG->proxypassword . '@' . $proxy; + } + } + return $proxy; + } + + /** + * Configure the provided AWS client to route traffic via the moodle proxy for any hosts not excluded. + * + * @param AwsClient $client + * @return AwsClient + */ + public static function configure_client_proxy(AwsClient $client): AwsClient { + $client->getHandlerList()->appendBuild(self::add_proxy_when_required(), 'proxy_bypass'); + return $client; + } + + /** + * Generate a middleware higher order function to wrap the handler and append proxy configuration based on target. + * + * @return callable Middleware high order callable. + */ + protected static function add_proxy_when_required(): callable { + return function (callable $fn) { + return function (CommandInterface $command, ?RequestInterface $request = null) use ($fn) { + if (isset($request)) { + $target = (string) $request->getUri(); + if (!is_proxybypass($target)) { + $command['@http']['proxy'] = self::get_proxy_string(); + } + } + + $promise = $fn($command, $request); + return $promise; + }; + }; + } +} diff --git a/classes/client_factory.php b/classes/client_factory.php new file mode 100644 index 0000000..d17d7a8 --- /dev/null +++ b/classes/client_factory.php @@ -0,0 +1,62 @@ +. + +/** + * AWS Client factory. Retrieves a client with moodle specific HTTP configuration. + * + * @package tool_cloudmetrics + * @author Peter Burnett + * @copyright 2022 Catalyst IT + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace tool_cloudmetrics; +use \Aws\AwsClient; + +/** + * AWS Client factory. Retrieves a client with moodle specific HTTP configuration. + * + * @copyright 2022 Catalyst IT + * @author Peter Burnett + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class client_factory { + /** + * Get an AWS client with moodle specific HTTP configuration. + * + * @param string $class Fully qualified AWS classname e.g. \Aws\S3\S3Client + * @param array $opts array of constructor options for AWS Client. + * @return AwsClient + */ + public static function get_client(string $class, array $opts): AwsClient { + // Modify the opts to add HTTP timeouts. + if (empty($opts['http'])) { + $opts['http'] = ['connect_timeout' => HOURSECS]; + } else if (is_array($opts['http'] && !array_key_exists('connect_timeout', $opts['http']))) { + // Try not to override existing settings. + $opts['http']['connect_timeout'] = HOURSECS; + } + + // Blindly trust the call here. If it exceptions, the raw message is the most useful. + $client = new $class($opts); + if (!$client instanceof \Aws\AwsClient) { + throw new \moodle_exception('clientnotfound', 'tool_cloudmetrics'); + } + + // Now we can configure the proxy with the routing aware middleware. + return aws_helper::configure_client_proxy($client); + } +} diff --git a/collector/cloudwatch/classes/collector.php b/collector/cloudwatch/classes/collector.php index 6c16efd..8be6716 100644 --- a/collector/cloudwatch/classes/collector.php +++ b/collector/cloudwatch/classes/collector.php @@ -16,15 +16,9 @@ namespace cltr_cloudwatch; -use local_aws\local\client_factory; use tool_cloudmetrics\collector\base; use tool_cloudmetrics\metric\metric_item; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/local/aws/sdk/aws-autoloader.php'); - - +use tool_cloudmetrics\client_factory; /** * Collector class for AWS Cloudwatch. * @@ -130,6 +124,6 @@ public function is_ready(): bool { return false; } - return lib::is_plugin_usable() && !is_null(self::$client); + return !is_null(self::$client); } } diff --git a/collector/cloudwatch/classes/lib.php b/collector/cloudwatch/classes/lib.php index ee917c7..c102488 100644 --- a/collector/cloudwatch/classes/lib.php +++ b/collector/cloudwatch/classes/lib.php @@ -27,18 +27,6 @@ class lib { /** @var string AWS version*/ const AWS_VERSION = '2010-08-01'; - /** @var int Local AWS version*/ - const LOCAL_AWS_VERSION = 2022033100; - - /** - * Returns if the plugin can be used. - * - * @return bool - */ - public static function is_plugin_usable() { - return (class_exists('\local_aws\local\client_factory') && - class_exists('\local_aws\admin_settings_aws_region')); - } /** * Get the configuration values for the plugin, substituting in defaults where diff --git a/collector/cloudwatch/lang/en/cltr_cloudwatch.php b/collector/cloudwatch/lang/en/cltr_cloudwatch.php index 91b2335..d4f88d1 100644 --- a/collector/cloudwatch/lang/en/cltr_cloudwatch.php +++ b/collector/cloudwatch/lang/en/cltr_cloudwatch.php @@ -52,5 +52,3 @@ // Missing requirements. $string['unsatisfied_requirements'] = 'Unsatisfied Requirements'; -$string['aws:installneeded'] = 'Plugin \'local_aws\' of version {$a} is required.'; -$string['aws:upgradeneeded'] = 'Plugin \'local_aws\' needs to be upgraded to version {$a}.'; diff --git a/collector/cloudwatch/settings.php b/collector/cloudwatch/settings.php index c8b4f5e..bf15ea6 100644 --- a/collector/cloudwatch/settings.php +++ b/collector/cloudwatch/settings.php @@ -29,66 +29,48 @@ if ($ADMIN->fulltree) { - // Some of the settings cannot be created if the plugin is not fully usable. - if (\cltr_cloudwatch\lib::is_plugin_usable()) { - $settings->add(new admin_setting_heading('cltr_cloudwatch_settings', '', - get_string('pluginnamedesc', 'cltr_cloudwatch'))); + $settings->add(new admin_setting_heading('cltr_cloudwatch_settings', '', + get_string('pluginnamedesc', 'cltr_cloudwatch'))); - // AWS settings. - $settings->add(new admin_setting_heading('cltr_cloudwatch_aws', - get_string('awssettings', 'cltr_cloudwatch'), - get_string('awssettings_desc', 'cltr_cloudwatch') - )); + // AWS settings. + $settings->add(new admin_setting_heading('cltr_cloudwatch_aws', + get_string('awssettings', 'cltr_cloudwatch'), + get_string('awssettings_desc', 'cltr_cloudwatch') + )); - $settings->add(new \local_aws\admin_settings_aws_region('cltr_cloudwatch/awsregion', - get_string('awsregion', 'cltr_cloudwatch'), - get_string('awsregion_desc', 'cltr_cloudwatch'), - 'ap-southeast-2' - )); + $settings->add(new \tool_cloudmetrics\admin_settings_aws_region('cltr_cloudwatch/awsregion', + get_string('awsregion', 'cltr_cloudwatch'), + get_string('awsregion_desc', 'cltr_cloudwatch'), + 'ap-southeast-2' + )); - $settings->add(new admin_setting_configtext('cltr_cloudwatch/aws_key', - get_string('awskey', 'cltr_cloudwatch'), - get_string('awskey_desc', 'cltr_cloudwatch'), - '', PARAM_TEXT)); + $settings->add(new admin_setting_configtext('cltr_cloudwatch/aws_key', + get_string('awskey', 'cltr_cloudwatch'), + get_string('awskey_desc', 'cltr_cloudwatch'), + '', PARAM_TEXT)); - $settings->add(new admin_setting_configpasswordunmask('cltr_cloudwatch/aws_secret', - get_string('awssecret', 'cltr_cloudwatch'), - get_string('awssecret_desc', 'cltr_cloudwatch'), - '')); + $settings->add(new admin_setting_configpasswordunmask('cltr_cloudwatch/aws_secret', + get_string('awssecret', 'cltr_cloudwatch'), + get_string('awssecret_desc', 'cltr_cloudwatch'), + '')); - // General Settings. - $settings->add(new admin_setting_heading('cltr_cloudwatch_general', - get_string('generalsettings', 'cltr_cloudwatch'), - get_string('generalsettings_desc', 'cltr_cloudwatch') - )); + // General Settings. + $settings->add(new admin_setting_heading('cltr_cloudwatch_general', + get_string('generalsettings', 'cltr_cloudwatch'), + get_string('generalsettings_desc', 'cltr_cloudwatch') + )); - // Namespace. - $settings->add(new admin_setting_configtext('cltr_cloudwatch/namespace', - get_string('namespace', 'cltr_cloudwatch'), - get_string('namespace_desc', 'cltr_cloudwatch'), - '', PARAM_TEXT)); + // Namespace. + $settings->add(new admin_setting_configtext('cltr_cloudwatch/namespace', + get_string('namespace', 'cltr_cloudwatch'), + get_string('namespace_desc', 'cltr_cloudwatch'), + '', PARAM_TEXT)); - // Environment. - $settings->add(new admin_setting_configtext('cltr_cloudwatch/environment', - get_string('environment', 'cltr_cloudwatch'), - get_string('environment_desc', 'cltr_cloudwatch'), - 'Dev', PARAM_TEXT)); - } else { - $plugininfo = $plugins = \core_plugin_manager::instance()->get_plugin_info('local_aws'); - if (is_null($plugininfo)) { - $text = $OUTPUT->notification(get_string('aws:installneeded', 'cltr_cloudwatch', \cltr_cloudwatch\lib::LOCAL_AWS_VERSION)); - $settings->add(new \admin_setting_heading('cltr_cloudwatch_aws', - get_string('unsatisfied_requirements', 'cltr_cloudwatch'), - $text - )); - } else if ($plugininfo->versiondisk < \cltr_cloudwatch\lib::LOCAL_AWS_VERSION) { - $text = $OUTPUT->notification(get_string('aws:upgradeneeded', 'cltr_cloudwatch', \cltr_cloudwatch\lib::LOCAL_AWS_VERSION)); - $settings->add(new \admin_setting_heading('cltr_cloudwatch_aws', - get_string('unsatisfied_requirements', 'cltr_cloudwatch'), - $text - )); - } - } + // Environment. + $settings->add(new admin_setting_configtext('cltr_cloudwatch/environment', + get_string('environment', 'cltr_cloudwatch'), + get_string('environment_desc', 'cltr_cloudwatch'), + 'Dev', PARAM_TEXT)); } } diff --git a/lang/en/tool_cloudmetrics.php b/lang/en/tool_cloudmetrics.php index aebe18a..f28038a 100644 --- a/lang/en/tool_cloudmetrics.php +++ b/lang/en/tool_cloudmetrics.php @@ -112,6 +112,7 @@ $string['metric_enabled'] = 'Metric \'{$a}\' enabled'; $string['metric_not_enabled'] = 'Metric \'{$a}\' not enabled'; $string['metric_not_found'] = 'Metric \'{$a}\' not found'; +$string['clientnotfound'] = 'Client not found'; $string['collector_failed'] = 'Collector \'{$a->name}\' failed {$a->time}'; $string['collector_passed'] = 'Collector \'{$a->name}\' succeeded since {$a->time}'; $string['collector_never'] = 'Collector \'{$a}\' has never executed'; diff --git a/version.php b/version.php index 73ed212..f7e85d0 100644 --- a/version.php +++ b/version.php @@ -25,12 +25,12 @@ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2022082501; -$plugin->release = 2022082501; +$plugin->version = 2024111900; +$plugin->release = 2024111900; -$plugin->requires = 2017051500; // Our lowest supported Moodle (3.3.0). +$plugin->requires = 2024042200; // Our lowest supported Moodle (4.4.0). -$plugin->supported = [35, 401]; // Available as of Moodle 3.9.0 or later. +$plugin->supported = [404, 405]; // Available as of Moodle 4.4.0 or later. // TODO $plugin->incompatible = ; // Available as of Moodle 3.9.0 or later. $plugin->component = 'tool_cloudmetrics';