From de79a68ddac471fc541ea5bd78bb154d46265f0c Mon Sep 17 00:00:00 2001 From: Florimond Manca Date: Thu, 22 Oct 2020 12:15:58 +0200 Subject: [PATCH] Add Azure IoT Edge integration (#7465) * Add skeleton * Working Docker setup for 1.0.9 * Attempt 1.0.10-rc2 setup * Finalize RC2 dev setup * Fix double-endpoint setup, implement scraping of Prometheus endpoints * Update CI config * Add config class, add failing integration test * Successfully collect and test metrics, improve env up/down robustness * Make tests pass * Use local mock server for CI tests * Add Edge Agent metrics * Update codecov config * Tweak exclude_labels * Fix invalid manifest * Add edgeHub metrics * Document mock server metrics generation * Fix Python 2 tests compatibility * Assert E2E tags * Skip E2E tests if IOT_EDGE_CONNSTR is missing * Use Windows-compatible mock server setup * Add security daemon health service check * Simplify prometheus url config, add config tests * Fix style, fix Windows test compat * Verify service check in e2e * Fix check class name case * Add config spec * Add logs to config spec and test env * Use auto-discovery for log collection * Enable log collection via Docker labels * Set required properties in config spec * Reorganize config options order * Loosen wait conditions * Update namespace to azure.iot_edge * Add version metadata collection * Update manifest.json * Check types * Write up metadata.csv * Fill in service_checks.json * Add TLS support to E2E environment * Add code comment about single-instance and composition approaches * Drop note about setting certs in config.yaml This is already done automatically by the E2E environment * Write up README * Lingo: security daemon -> security manager * Add recommended monitors * Apply no-brainer suggestions Co-authored-by: Florian Veaux * Update version metadata transformer * Address feedback Drop security manager service check Reorganize check as an OpenMetricsBaseCheck subclass Fix E2E tests Update docs Fix service checks: can_connect -> prometheus.health * Move instance config to Edge Agent labels * Apply suggestions from docs review Co-authored-by: Kari Halsted <12926135+kayayarai@users.noreply.github.com> * Fix type of renotify_interval in monitors json Co-authored-by: Florian Veaux Co-authored-by: Kari Halsted <12926135+kayayarai@users.noreply.github.com> --- .azure-pipelines/changes.yml | 2 +- .../templates/test-all-checks.yml | 6 + .codecov.yml | 9 + azure_iot_edge/CHANGELOG.md | 2 + azure_iot_edge/MANIFEST.in | 10 + azure_iot_edge/README.md | 114 +++++ azure_iot_edge/assets/configuration/spec.yaml | 24 ++ .../dashboards/azure_iot_edge_overview.json | 0 .../assets/monitors/disk_usage.json | 31 ++ .../assets/monitors/edgehub_retries.json | 32 ++ .../assets/monitors/iothub_syncs.json | 32 ++ .../assets/monitors/memory_usage.json | 31 ++ azure_iot_edge/assets/service_checks.json | 32 ++ azure_iot_edge/datadog_checks/__init__.py | 4 + .../azure_iot_edge/__about__.py | 4 + .../datadog_checks/azure_iot_edge/__init__.py | 7 + .../datadog_checks/azure_iot_edge/check.py | 22 + .../datadog_checks/azure_iot_edge/config.py | 63 +++ .../azure_iot_edge/data/conf.yaml.example | 53 +++ .../datadog_checks/azure_iot_edge/metrics.py | 56 +++ .../datadog_checks/azure_iot_edge/types.py | 14 + azure_iot_edge/images/.gitkeep | 0 azure_iot_edge/manifest.json | 41 ++ azure_iot_edge/metadata.csv | 60 +++ azure_iot_edge/requirements-dev.txt | 1 + azure_iot_edge/requirements.in | 0 azure_iot_edge/setup.py | 64 +++ azure_iot_edge/tests/README.md | 146 +++++++ azure_iot_edge/tests/__init__.py | 3 + azure_iot_edge/tests/common.py | 383 +++++++++++++++++ .../tests/compose/device/Dockerfile | 39 ++ azure_iot_edge/tests/compose/device/rund.sh | 50 +++ .../tests/compose/docker-compose-tls.yaml | 35 ++ .../tests/compose/docker-compose.yaml | 28 ++ .../mock_server/data/metrics/edge_agent.txt | 123 ++++++ .../mock_server/data/metrics/edge_hub.txt | 121 ++++++ .../mock_server/docker-compose-windows.yaml | 10 + .../compose/mock_server/docker-compose.yaml | 10 + azure_iot_edge/tests/conftest.py | 104 +++++ azure_iot_edge/tests/e2e_utils.py | 62 +++ azure_iot_edge/tests/test_check.py | 108 +++++ azure_iot_edge/tests/test_config.py | 83 ++++ azure_iot_edge/tests/test_e2e.py | 26 ++ azure_iot_edge/tests/tls/.gitignore | 9 + azure_iot_edge/tests/tls/README.md | 31 ++ azure_iot_edge/tests/tls/certGen.sh | 394 ++++++++++++++++++ .../tls/openssl_device_intermediate_ca.cnf | 127 ++++++ azure_iot_edge/tests/tls/openssl_root_ca.cnf | 127 ++++++ azure_iot_edge/tests/tls/setup.sh | 30 ++ azure_iot_edge/tox.ini | 35 ++ 50 files changed, 2797 insertions(+), 1 deletion(-) create mode 100644 azure_iot_edge/CHANGELOG.md create mode 100644 azure_iot_edge/MANIFEST.in create mode 100644 azure_iot_edge/README.md create mode 100644 azure_iot_edge/assets/configuration/spec.yaml create mode 100644 azure_iot_edge/assets/dashboards/azure_iot_edge_overview.json create mode 100644 azure_iot_edge/assets/monitors/disk_usage.json create mode 100644 azure_iot_edge/assets/monitors/edgehub_retries.json create mode 100644 azure_iot_edge/assets/monitors/iothub_syncs.json create mode 100644 azure_iot_edge/assets/monitors/memory_usage.json create mode 100644 azure_iot_edge/assets/service_checks.json create mode 100644 azure_iot_edge/datadog_checks/__init__.py create mode 100644 azure_iot_edge/datadog_checks/azure_iot_edge/__about__.py create mode 100644 azure_iot_edge/datadog_checks/azure_iot_edge/__init__.py create mode 100644 azure_iot_edge/datadog_checks/azure_iot_edge/check.py create mode 100644 azure_iot_edge/datadog_checks/azure_iot_edge/config.py create mode 100644 azure_iot_edge/datadog_checks/azure_iot_edge/data/conf.yaml.example create mode 100644 azure_iot_edge/datadog_checks/azure_iot_edge/metrics.py create mode 100644 azure_iot_edge/datadog_checks/azure_iot_edge/types.py create mode 100644 azure_iot_edge/images/.gitkeep create mode 100644 azure_iot_edge/manifest.json create mode 100644 azure_iot_edge/metadata.csv create mode 100644 azure_iot_edge/requirements-dev.txt create mode 100644 azure_iot_edge/requirements.in create mode 100644 azure_iot_edge/setup.py create mode 100644 azure_iot_edge/tests/README.md create mode 100644 azure_iot_edge/tests/__init__.py create mode 100644 azure_iot_edge/tests/common.py create mode 100644 azure_iot_edge/tests/compose/device/Dockerfile create mode 100644 azure_iot_edge/tests/compose/device/rund.sh create mode 100644 azure_iot_edge/tests/compose/docker-compose-tls.yaml create mode 100644 azure_iot_edge/tests/compose/docker-compose.yaml create mode 100644 azure_iot_edge/tests/compose/mock_server/data/metrics/edge_agent.txt create mode 100644 azure_iot_edge/tests/compose/mock_server/data/metrics/edge_hub.txt create mode 100644 azure_iot_edge/tests/compose/mock_server/docker-compose-windows.yaml create mode 100644 azure_iot_edge/tests/compose/mock_server/docker-compose.yaml create mode 100644 azure_iot_edge/tests/conftest.py create mode 100644 azure_iot_edge/tests/e2e_utils.py create mode 100644 azure_iot_edge/tests/test_check.py create mode 100644 azure_iot_edge/tests/test_config.py create mode 100644 azure_iot_edge/tests/test_e2e.py create mode 100644 azure_iot_edge/tests/tls/.gitignore create mode 100644 azure_iot_edge/tests/tls/README.md create mode 100755 azure_iot_edge/tests/tls/certGen.sh create mode 100644 azure_iot_edge/tests/tls/openssl_device_intermediate_ca.cnf create mode 100644 azure_iot_edge/tests/tls/openssl_root_ca.cnf create mode 100755 azure_iot_edge/tests/tls/setup.sh create mode 100644 azure_iot_edge/tox.ini diff --git a/.azure-pipelines/changes.yml b/.azure-pipelines/changes.yml index 615f7fa25162e..2800c7da04131 100644 --- a/.azure-pipelines/changes.yml +++ b/.azure-pipelines/changes.yml @@ -27,7 +27,7 @@ jobs: - template: './templates/test-single-windows.yml' parameters: job_name: Changed - check: '--changed datadog_checks_base datadog_checks_dev active_directory aspdotnet disk dns_check dotnetclr exchange_server iis pdh_check sqlserver tcp_check win32_event_log windows_service wmi_check' + check: '--changed datadog_checks_base datadog_checks_dev active_directory aspdotnet azure_iot_edge disk dns_check dotnetclr exchange_server iis pdh_check sqlserver tcp_check win32_event_log windows_service wmi_check' display: Windows pip_cache_config: key: 'pip | $(Agent.OS) | datadog_checks_base/datadog_checks/base/data/agent_requirements.in' diff --git a/.azure-pipelines/templates/test-all-checks.yml b/.azure-pipelines/templates/test-all-checks.yml index d6717d153cb45..b07e2436f8089 100644 --- a/.azure-pipelines/templates/test-all-checks.yml +++ b/.azure-pipelines/templates/test-all-checks.yml @@ -49,6 +49,12 @@ jobs: - checkName: aspdotnet displayName: ASP.NET os: windows + - checkName: azure_iot_edge + displayName: Azure IoT Edge + os: linux + - checkName: azure_iot_edge + displayName: Azure IoT Edge + os: windows - checkName: btrfs displayName: Btrfs os: linux diff --git a/.codecov.yml b/.codecov.yml index a382f2e3e8537..49334c9be7f2c 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -51,6 +51,10 @@ coverage: target: 75 flags: - apache + Azure IoT Edge: + target: 75 + flags: + - azure_iot_edge Btrfs: target: 75 flags: @@ -565,6 +569,11 @@ flags: paths: - aspdotnet/datadog_checks/aspdotnet - aspdotnet/tests + azure_iot_edge: + carryforward: true + paths: + - azure_iot_edge/datadog_checks/azure_iot_edge + - azure_iot_edge/tests btrfs: carryforward: true paths: diff --git a/azure_iot_edge/CHANGELOG.md b/azure_iot_edge/CHANGELOG.md new file mode 100644 index 0000000000000..0233a99fe9ae4 --- /dev/null +++ b/azure_iot_edge/CHANGELOG.md @@ -0,0 +1,2 @@ +# CHANGELOG - Azure IoT Edge + diff --git a/azure_iot_edge/MANIFEST.in b/azure_iot_edge/MANIFEST.in new file mode 100644 index 0000000000000..6fa1c2388a4eb --- /dev/null +++ b/azure_iot_edge/MANIFEST.in @@ -0,0 +1,10 @@ +graft datadog_checks +graft tests + +include MANIFEST.in +include README.md +include requirements.in +include requirements-dev.txt +include manifest.json + +global-exclude *.py[cod] __pycache__ diff --git a/azure_iot_edge/README.md b/azure_iot_edge/README.md new file mode 100644 index 0000000000000..9043fd7e57f92 --- /dev/null +++ b/azure_iot_edge/README.md @@ -0,0 +1,114 @@ +# Agent Check: Azure IoT Edge + +## Overview + +[Azure IoT Edge][1] is a fully managed service to deploy Cloud workloads to run on Internet of Things (IoT) Edge devices via standard containers. + +Use the Datadog-Azure IoT Edge integration to collect metrics and health status from IoT Edge devices. + +**Note**: This integration requires IoT Edge runtime version 1.0.10 or above. + +## Setup + +Follow the instructions below to install and configure this check for an IoT Edge device running on a device host. + +### Installation + +The Azure IoT Edge check is included in the [Datadog Agent][2] package. + +No additional installation is needed on your device. + +### Configuration + +Configure the IoT Edge device so that the Agent runs as a custom module. Follow the Microsoft documentation on [deploying Azure IoT Edge modules][3] for information on installing and working with custom modules for Azure IoT Edge. + +Follow the steps below to configure the IoT Edge device, runtime modules, and the Datadog Agent to start collecting IoT Edge metrics. + +1. Configure the **Edge Agent** runtime module as follows: + - Image version must be `1.0.10` or above. + - Under "Create Options", add the following `Labels`. Edit the `com.datadoghq.ad.instances` label as appropriate. See the [sample azure_iot_edge.d/conf.yaml][5] for all available configuration options. See the documentation on [Docker Integrations Autodiscovery][6] for more information on labels-based integration configuration. + + ```json + "Labels": { + "com.datadoghq.ad.check_names": "[\"azure_iot_edge\"]", + "com.datadoghq.ad.init_configs": "[{}]", + "com.datadoghq.ad.instances": "[{\"edge_hub_prometheus_url\": \"http://edgeHub:9600/metrics\", \"edge_agent_prometheus_url\": \"http://edgeAgent:9600/metrics\"}]" + } + ``` + + - Under "Environment Variables", enable experimental metrics by adding these environment variables (note the double underscores): + - `ExperimentalFeatures__Enabled`: `true` + - `ExperimentalFeatures__EnableMetrics`: `true` + +1. Configure the **Edge Hub** runtime module as follows: + - Image version must be `1.0.10` or above. + - Under "Environment Variables", enable experimental metrics by adding these environment variables (note the double underscores): + - `ExperimentalFeatures__Enabled`: `true` + - `ExperimentalFeatures__EnableMetrics`: `true` + +1. Install and configure the Datadog Agent as a **custom module**: + - Set the module name. For example: `datadog-agent`. + - Set the Agent image URI. For example: `datadog/agent:7`. + - Under "Environment Variables", configure your `DD_API_KEY`. You may also set extra Agent configuration here (see [Agent Environment Variables][4]). + - Under "Container Create Options", enter the following configuration based on your device OS. **Note**: `NetworkId` must correspond to the network name set in the device `config.yaml` file. + + - Linux: + ```json + { + "HostConfig": { + "NetworkMode": "default", + "Env": ["NetworkId=azure-iot-edge"], + "Binds": ["/var/run/docker.sock:/var/run/docker.sock"] + } + } + ``` + - Windows: + ```json + { + "HostConfig": { + "NetworkMode": "default", + "Env": ["NetworkId=nat"], + "Binds": ["//./pipe/iotedge_moby_engine:/./pipe/docker_engine"] + } + } + ``` + + - Save the Datadog Agent custom module. + +1. Save and deploy changes to your device configuration. + +### Validation + +Once the Agent has been deployed to the device, [run the Agent's status subcommand][7] and look for `azure_iot_edge` under the Checks section. + +## Data Collected + +### Metrics + +See [metadata.csv][8] for a list of metrics provided by this check. + +### Service Checks + +**azure.iot_edge.edge_agent.prometheus.health**: +Returns `CRITICAL` if the Agent is unable to reach the Edge Agent metrics Prometheus endpoint. Returns `OK` otherwise. + +**azure.iot_edge.edge_hub.prometheus.health**: +Returns `CRITICAL` if the Agent is unable to reach the Edge Hub metrics Prometheus endpoint. Returns `OK` otherwise. + +### Events + +Azure IoT Edge does not include any events. + +## Troubleshooting + +Need help? Contact [Datadog support][9]. + +[1]: https://azure.microsoft.com/en-us/services/iot-edge/ +[2]: https://docs.datadoghq.com/agent/ +[3]: https://docs.microsoft.com/en-us/azure/iot-edge/how-to-deploy-modules-portal +[4]: https://docs.datadoghq.com/agent/guide/environment-variables/ +[5]: https://github.com/DataDog/integrations-core/blob/master/azure_iot_edge/datadog_checks/azure_iot_edge/data/conf.yaml.example +[6]: https://docs.datadoghq.com/agent/docker/integrations/ +[7]: https://docs.datadoghq.com/agent/guide/agent-commands/#agent-status-and-information +[8]: https://github.com/DataDog/integrations-core/blob/master/azure_iot_edge/metadata.csv +[9]: https://docs.datadoghq.com/help/ diff --git a/azure_iot_edge/assets/configuration/spec.yaml b/azure_iot_edge/assets/configuration/spec.yaml new file mode 100644 index 0000000000000..a5955299e99dc --- /dev/null +++ b/azure_iot_edge/assets/configuration/spec.yaml @@ -0,0 +1,24 @@ +name: Azure IoT Edge +files: + - name: azure_iot_edge.yaml + options: + - template: init_config + options: + - template: init_config/default + - template: instances + options: + - name: edge_hub_prometheus_url + description: | + The URL where Edge Hub metrics are exposed via Prometheus. + required: true + value: + type: string + example: http://edgeHub:9600/metrics + - name: edge_agent_prometheus_url + description: | + The URL where Edge Agent metrics are exposed via Prometheus. + required: true + value: + type: string + example: http://edgeAgent:9600/metrics + - template: instances/default diff --git a/azure_iot_edge/assets/dashboards/azure_iot_edge_overview.json b/azure_iot_edge/assets/dashboards/azure_iot_edge_overview.json new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/azure_iot_edge/assets/monitors/disk_usage.json b/azure_iot_edge/assets/monitors/disk_usage.json new file mode 100644 index 0000000000000..b8ca32172a363 --- /dev/null +++ b/azure_iot_edge/assets/monitors/disk_usage.json @@ -0,0 +1,31 @@ +{ + "name": "[Azure IoT Edge] IoT Edge device {{host}} is running out of available disk space", + "type": "query alert", + "query": "max(last_1h):avg:azure.iot_edge.edge_agent.available_disk_space_bytes{*} by {host} / avg:azure.iot_edge.edge_agent.total_disk_space_bytes{*} by {host}.rollup(max, 60) * 100 < 10", + "message": "Please check device {{host}}, as Edge Agent reports that available disk space has dropped below {{threshold}}%.", + "tags": [ + "integration:azure_iot_edge" + ], + "options": { + "notify_audit": false, + "locked": false, + "timeout_h": 0, + "silenced": {}, + "include_tags": true, + "no_data_timeframe": null, + "require_full_window": true, + "new_host_delay": 300, + "notify_no_data": false, + "renotify_interval": 0, + "escalation_message": "", + "thresholds": { + "critical": 10, + "warning": 25, + "critical_recovery": 11, + "warning_recovery": 26 + } + }, + "recommended_monitor_metadata": { + "description": "Triggers an alert when an IoT Edge device is running out of available disk space" + } +} diff --git a/azure_iot_edge/assets/monitors/edgehub_retries.json b/azure_iot_edge/assets/monitors/edgehub_retries.json new file mode 100644 index 0000000000000..7356b47c51145 --- /dev/null +++ b/azure_iot_edge/assets/monitors/edgehub_retries.json @@ -0,0 +1,32 @@ +{ + "name": "[Azure IoT Edge] Rate of Edge Hub operations retries is higher than usual on device device {{host}}", + "type": "query alert", + "query": "avg(last_1h):anomalies(per_minute(avg:azure.iot_edge.edge_hub.operation_retry_total{*} by {host}), 'basic', 2, direction='above', alert_window='last_15m', interval=60, count_default_zero='true') >= 1", + "message": "Please check device {{host}}, as Edge Hub reports a rate of operation retries of {{value}} per minute, which is higher than usual.", + "tags": [ + "integration:azure_iot_edge" + ], + "options": { + "notify_audit": false, + "locked": false, + "timeout_h": 0, + "new_host_delay": 300, + "require_full_window": false, + "notify_no_data": false, + "renotify_interval": 0, + "escalation_message": "", + "no_data_timeframe": null, + "include_tags": true, + "thresholds": { + "critical": 1, + "critical_recovery": 0 + }, + "threshold_windows": { + "trigger_window": "last_15m", + "recovery_window": "last_15m" + } + }, + "recommended_monitor_metadata": { + "description": "Notifies when rate of Edge Hub operation retries is higher than usual" + } +} diff --git a/azure_iot_edge/assets/monitors/iothub_syncs.json b/azure_iot_edge/assets/monitors/iothub_syncs.json new file mode 100644 index 0000000000000..65aaa4c6eb18c --- /dev/null +++ b/azure_iot_edge/assets/monitors/iothub_syncs.json @@ -0,0 +1,32 @@ +{ + "name": "[Azure IoT Edge] Rate of unsuccessful syncs with IoT Hub is higher than usual on device {{host}}", + "type": "query alert", + "query": "avg(last_1h):anomalies(per_minute(avg:azure.iot_edge.edge_agent.unsuccessful_iothub_syncs_total{*} by {host}), 'basic', 2, direction='above', alert_window='last_15m', interval=60, count_default_zero='true') >= 1", + "message": "Number of unsuccessful syncs between Edge Agent and IoT Hub on device {{host}} is at {{value}} per minute, which is higher than usual.", + "tags": [ + "integration:azure_iot_edge" + ], + "options": { + "notify_audit": false, + "locked": false, + "timeout_h": 0, + "new_host_delay": 300, + "require_full_window": false, + "notify_no_data": false, + "renotify_interval": 0, + "escalation_message": "", + "no_data_timeframe": null, + "include_tags": true, + "thresholds": { + "critical": 1, + "critical_recovery": 0 + }, + "threshold_windows": { + "trigger_window": "last_15m", + "recovery_window": "last_15m" + } + }, + "recommended_monitor_metadata": { + "description": "Notifies when unsuccessful syncs between Edge Agent and IoT Hub are higher than usual" + } +} diff --git a/azure_iot_edge/assets/monitors/memory_usage.json b/azure_iot_edge/assets/monitors/memory_usage.json new file mode 100644 index 0000000000000..b40035c85a9c8 --- /dev/null +++ b/azure_iot_edge/assets/monitors/memory_usage.json @@ -0,0 +1,31 @@ +{ + "name": "[Azure IoT Edge] IoT Edge device {{host}} is running out of memory", + "type": "query alert", + "query": "max(last_1h):avg:azure.iot_edge.edge_agent.used_memory_bytes{*} by {host} / avg:azure.iot_edge.edge_agent.total_memory_bytes{*} by {host}.rollup(max, 60) * 100 > 80", + "message": "Please check device {{host}}, as Edge Agent reports usage of more than {{threshold}}% of available RAM for the last hour.", + "tags": [ + "integration:azure_iot_edge" + ], + "options": { + "notify_audit": false, + "locked": false, + "timeout_h": 0, + "silenced": {}, + "include_tags": true, + "no_data_timeframe": null, + "require_full_window": true, + "new_host_delay": 300, + "notify_no_data": false, + "renotify_interval": 0, + "escalation_message": "", + "thresholds": { + "critical": 80, + "warning": 65, + "critical_recovery": 79, + "warning_recovery": 64 + } + }, + "recommended_monitor_metadata": { + "description": "Triggers an alert when an IoT Edge device is running out of memory" + } +} diff --git a/azure_iot_edge/assets/service_checks.json b/azure_iot_edge/assets/service_checks.json new file mode 100644 index 0000000000000..8168f3622b290 --- /dev/null +++ b/azure_iot_edge/assets/service_checks.json @@ -0,0 +1,32 @@ +[ + { + "agent_version": "6.24.0", + "integration": "Azure IoT Edge", + "groups": [ + "host", + "endpoint" + ], + "check": "azure.iot_edge.edge_agent.prometheus.health", + "statuses": [ + "ok", + "critical" + ], + "name": "Edge Agent health", + "description": "Returns `CRITICAL` if the Agent is unable to reach the Edge Agent metrics Prometheus endpoint. Returns `OK` otherwise." + }, + { + "agent_version": "6.24.0", + "integration": "Azure IoT Edge", + "groups": [ + "host", + "endpoint" + ], + "check": "azure.iot_edge.edge_hub.prometheus.health", + "statuses": [ + "ok", + "critical" + ], + "name": "Edge Hub health", + "description": "Returns `CRITICAL` if the Agent is unable to reach the Edge Hub metrics Prometheus endpoint. Returns `OK` otherwise." + } +] diff --git a/azure_iot_edge/datadog_checks/__init__.py b/azure_iot_edge/datadog_checks/__init__.py new file mode 100644 index 0000000000000..cdddf032324d5 --- /dev/null +++ b/azure_iot_edge/datadog_checks/__init__.py @@ -0,0 +1,4 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore diff --git a/azure_iot_edge/datadog_checks/azure_iot_edge/__about__.py b/azure_iot_edge/datadog_checks/azure_iot_edge/__about__.py new file mode 100644 index 0000000000000..e675c84da2568 --- /dev/null +++ b/azure_iot_edge/datadog_checks/azure_iot_edge/__about__.py @@ -0,0 +1,4 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +__version__ = '0.0.1' diff --git a/azure_iot_edge/datadog_checks/azure_iot_edge/__init__.py b/azure_iot_edge/datadog_checks/azure_iot_edge/__init__.py new file mode 100644 index 0000000000000..95089d028a868 --- /dev/null +++ b/azure_iot_edge/datadog_checks/azure_iot_edge/__init__.py @@ -0,0 +1,7 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from .__about__ import __version__ +from .check import AzureIoTEdgeCheck + +__all__ = ['__version__', 'AzureIoTEdgeCheck'] diff --git a/azure_iot_edge/datadog_checks/azure_iot_edge/check.py b/azure_iot_edge/datadog_checks/azure_iot_edge/check.py new file mode 100644 index 0000000000000..6381d781cf649 --- /dev/null +++ b/azure_iot_edge/datadog_checks/azure_iot_edge/check.py @@ -0,0 +1,22 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from typing import cast + +from datadog_checks.base import OpenMetricsBaseCheck + +from .config import Config +from .types import Instance + + +class AzureIoTEdgeCheck(OpenMetricsBaseCheck): + __NAMESPACE__ = 'azure.iot_edge' # Child of `azure.` namespace. + + def __init__(self, name, init_config, instances): + self._config = Config(cast(Instance, instances[0])) + super(AzureIoTEdgeCheck, self).__init__(name, init_config, self._config.prometheus_instances) + + def check(self, _): + for instance in self._config.prometheus_instances: + scraper_config = self.get_scraper_config(instance) + self.process(scraper_config) diff --git a/azure_iot_edge/datadog_checks/azure_iot_edge/config.py b/azure_iot_edge/datadog_checks/azure_iot_edge/config.py new file mode 100644 index 0000000000000..9b8f21005ef8c --- /dev/null +++ b/azure_iot_edge/datadog_checks/azure_iot_edge/config.py @@ -0,0 +1,63 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from typing import List + +from datadog_checks.base import ConfigurationError +from datadog_checks.base.types import InstanceType + +from .metrics import EDGE_AGENT_METRICS, EDGE_HUB_METRICS +from .types import Instance + + +class Config(object): + """ + Hold instance configuration for a check. + + Encapsulates the validation of an `instance` dictionary while improving type information. + """ + + def __init__(self, instance): + # type: (Instance) -> None + tags = instance.get('tags', []) + + if not isinstance(tags, list): + raise ConfigurationError('tags {!r} must be a list (got {!r})'.format(tags, type(tags))) + + edge_hub_prometheus_url = instance.get('edge_hub_prometheus_url') + if not edge_hub_prometheus_url: + raise ConfigurationError('option "edge_hub_prometheus_url" is required') + + edge_hub_instance = self._create_prometheus_instance( + edge_hub_prometheus_url, namespace='edge_hub', metrics=EDGE_HUB_METRICS, tags=tags + ) + + edge_agent_prometheus_url = instance.get('edge_agent_prometheus_url') + if not edge_agent_prometheus_url: + raise ConfigurationError('option "edge_agent_prometheus_url" is required') + + edge_agent_instance = self._create_prometheus_instance( + edge_agent_prometheus_url, namespace='edge_agent', metrics=EDGE_AGENT_METRICS, tags=tags + ) + + # Configure version metadata collection. + edge_agent_instance['metadata_metric_name'] = 'edgeAgent_metadata' + edge_agent_instance['metadata_label_map'] = {'version': 'edge_agent_version'} + + self.prometheus_instances = [ + edge_hub_instance, + edge_agent_instance, + ] + + def _create_prometheus_instance(self, url, namespace, metrics, tags): + # type: (str, str, list, List[str]) -> InstanceType + return { + 'prometheus_url': url, + 'namespace': namespace, + 'metrics': metrics, + 'tags': tags, + 'exclude_labels': [ + 'ms_telemetry', # Always 'True'. + 'instance_number', # Random GUID, changes on device restart (risk of context explosion). + ], + } diff --git a/azure_iot_edge/datadog_checks/azure_iot_edge/data/conf.yaml.example b/azure_iot_edge/datadog_checks/azure_iot_edge/data/conf.yaml.example new file mode 100644 index 0000000000000..00147abbd73a3 --- /dev/null +++ b/azure_iot_edge/datadog_checks/azure_iot_edge/data/conf.yaml.example @@ -0,0 +1,53 @@ +## All options defined here are available to all instances. +# +init_config: + + ## @param service - string - optional + ## Attach the tag `service:` to every metric, event, and service check emitted by this integration. + ## + ## Additionally, this sets the default `service` for every log source. + # + # service: + +## Every instance is scheduled independent of the others. +# +instances: + + ## @param edge_hub_prometheus_url - string - required + ## The URL where Edge Hub metrics are exposed via Prometheus. + # + - edge_hub_prometheus_url: http://edgeHub:9600/metrics + + ## @param edge_agent_prometheus_url - string - required + ## The URL where Edge Agent metrics are exposed via Prometheus. + # + edge_agent_prometheus_url: http://edgeAgent:9600/metrics + + ## @param tags - list of strings - optional + ## A list of tags to attach to every metric and service check emitted by this instance. + ## + ## Learn more about tagging at https://docs.datadoghq.com/tagging + # + # tags: + # - : + # - : + + ## @param service - string - optional + ## Attach the tag `service:` to every metric, event, and service check emitted by this integration. + ## + ## Overrides any `service` defined in the `init_config` section. + # + # service: + + ## @param min_collection_interval - number - optional - default: 15 + ## This changes the collection interval of the check. For more information, see: + ## https://docs.datadoghq.com/developers/write_agent_check/#collection-interval + # + # min_collection_interval: 15 + + ## @param empty_default_hostname - boolean - optional - default: false + ## This forces the check to send metrics with no hostname. + ## + ## This is useful for cluster-level checks. + # + # empty_default_hostname: false diff --git a/azure_iot_edge/datadog_checks/azure_iot_edge/metrics.py b/azure_iot_edge/datadog_checks/azure_iot_edge/metrics.py new file mode 100644 index 0000000000000..3d8e1787caaff --- /dev/null +++ b/azure_iot_edge/datadog_checks/azure_iot_edge/metrics.py @@ -0,0 +1,56 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) + +# See: https://github.com/Azure/iotedge/blob/1.0.10-rc2/doc/BuiltInMetrics.md#edgehub +EDGE_HUB_METRICS = [ + { + 'edgehub_queue_length': 'queue_length', + 'edgehub_gettwin_total': 'gettwin_total', + 'edgehub_messages_received_total': 'messages_received_total', + 'edgehub_messages_sent_total': 'messages_sent_total', + 'edgehub_reported_properties_total': 'reported_properties_total', + 'edgehub_gettwin_duration_seconds': 'gettwin_duration_seconds', + 'edgehub_message_send_duration_seconds': 'message_send_duration_seconds', + 'edgehub_message_process_duration_seconds': 'message_process_duration_seconds', + 'edgehub_reported_properties_update_duration_seconds': 'reported_properties_update_duration_seconds', + 'edgehub_direct_method_duration_seconds': 'direct_method_duration_seconds', + 'edgehub_direct_methods_total': 'direct_methods_total', + 'edgehub_queue_length': 'queue_length', + 'edgehub_messages_dropped_total': 'messages_dropped_total', + 'edgehub_messages_unack_total': 'messages_unack_total', + 'edgehub_offline_count_total': 'offline_count_total', + 'edgehub_offline_duration_seconds': 'offline_duration_seconds', + 'edgehub_operation_retry_total': 'operation_retry_total', + 'edgehub_client_connect_failed_total': 'client_connect_failed_total', + }, +] + +# See: https://github.com/Azure/iotedge/blob/1.0.10-rc2/doc/BuiltInMetrics.md#edgeagent +EDGE_AGENT_METRICS = [ + { + # One copy for each module. + 'edgeAgent_total_time_running_correctly_seconds': 'total_time_running_correctly_seconds', + 'edgeAgent_total_time_expected_running_seconds': 'total_time_expected_running_seconds', + 'edgeAgent_command_latency_seconds': 'command_latency_seconds', + 'edgeAgent_available_disk_space_bytes': 'available_disk_space_bytes', + 'edgeAgent_total_disk_space_bytes': 'total_disk_space_bytes', + 'edgeAgent_used_memory_bytes': 'used_memory_bytes', + 'edgeAgent_total_memory_bytes': 'total_memory_bytes', + 'edgeAgent_used_cpu_percent': 'used_cpu_percent', + 'edgeAgent_created_pids_total': 'created_pids_total', + 'edgeAgent_total_network_in_bytes': 'total_network_in_bytes', + 'edgeAgent_total_network_out_bytes': 'total_network_out_bytes', + 'edgeAgent_total_disk_read_bytes': 'total_disk_read_bytes', + 'edgeAgent_total_disk_write_bytes': 'total_disk_write_bytes', + # One copy for each module, except the Edge Agent. + 'edgeAgent_module_start_total': 'module_start_total', + 'edgeAgent_module_stop_total': 'module_stop_total', + # Single copy. + 'edgeAgent_iothub_syncs_total': 'iothub_syncs_total', + 'edgeAgent_unsuccessful_iothub_syncs_total': 'unsuccessful_iothub_syncs_total', + 'edgeAgent_deployment_time_seconds': 'deployment_time_seconds', + 'edgeAgent_host_uptime_seconds': 'host_uptime_seconds', + 'edgeAgent_iotedged_uptime_seconds': 'iotedged_uptime_seconds', + }, +] diff --git a/azure_iot_edge/datadog_checks/azure_iot_edge/types.py b/azure_iot_edge/datadog_checks/azure_iot_edge/types.py new file mode 100644 index 0000000000000..f69c49c464ec0 --- /dev/null +++ b/azure_iot_edge/datadog_checks/azure_iot_edge/types.py @@ -0,0 +1,14 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from typing import List, TypedDict + +Instance = TypedDict( + 'Instance', + { + 'edge_hub_prometheus_url': str, + 'edge_agent_prometheus_url': str, + 'tags': List[str], + }, + total=False, +) diff --git a/azure_iot_edge/images/.gitkeep b/azure_iot_edge/images/.gitkeep new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/azure_iot_edge/manifest.json b/azure_iot_edge/manifest.json new file mode 100644 index 0000000000000..a3d97967286d7 --- /dev/null +++ b/azure_iot_edge/manifest.json @@ -0,0 +1,41 @@ +{ + "display_name": "Azure IoT Edge", + "maintainer": "help@datadoghq.com", + "manifest_version": "1.0.0", + "name": "azure_iot_edge", + "metric_prefix": "azure.iot_edge.", + "metric_to_check": "azure.iot_edge.edge_agent.iotedged_uptime_seconds", + "creates_events": false, + "short_description": "Monitor the health and performance of an Azure IoT Edge device and modules.", + "guid": "9eafeab9-daf4-4f54-befc-fcc623ec9c1b", + "support": "core", + "supported_os": [ + "linux", + "windows" + ], + "public_title": "Datadog-Azure IoT Edge Integration", + "categories": [ + "azure" + ], + "type": "check", + "is_public": false, + "integration_id": "azure-iot-edge", + "assets": { + "configuration": { + "spec": "assets/configuration/spec.yaml" + }, + "dashboards": {}, + "monitors": { + "Disk usage": "assets/monitors/disk_usage.json", + "Memory usage": "assets/monitors/memory_usage.json", + "IoT Hub syncs": "assets/monitors/iothub_syncs.json", + "Edge Hub retries": "assets/monitors/edgehub_retries.json" + }, + "saved_views": {}, + "service_checks": "assets/service_checks.json", + "logs": { + "source": "azure.iot_edge" + }, + "metrics_metadata": "metadata.csv" + } +} diff --git a/azure_iot_edge/metadata.csv b/azure_iot_edge/metadata.csv new file mode 100644 index 0000000000000..cf899fdfa9e98 --- /dev/null +++ b/azure_iot_edge/metadata.csv @@ -0,0 +1,60 @@ +metric_name,metric_type,interval,unit_name,per_unit_name,description,orientation,integration,short_name +azure.iot_edge.edge_hub.gettwin_total,count,,,,Total number of GetTwin calls.,0,azure_iot_edge, +azure.iot_edge.edge_hub.messages_received_total,count,,,,Total number of messages received from clients.,0,azure_iot_edge, +azure.iot_edge.edge_hub.messages_sent_total,count,,,,Total number of messages sent to clients of upstream.,0,azure_iot_edge, +azure.iot_edge.edge_hub.reported_properties_total,count,,,,Total reported property updates calls.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_size_bytes.count,gauge,,,,Count of message size from clients.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_size_bytes.sum,gauge,,,,Sum of message size from clients.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_size_bytes.quantile,gauge,,,,Quantile of message size from clients.,0,azure_iot_edge, +azure.iot_edge.edge_hub.gettwin_duration_seconds.count,gauge,,,,Count of time taken for get twin operations.,0,azure_iot_edge, +azure.iot_edge.edge_hub.gettwin_duration_seconds.sum,gauge,,,,Sum of time taken for get twin operations.,0,azure_iot_edge, +azure.iot_edge.edge_hub.gettwin_duration_seconds.quantile,gauge,,,,Quantile of time taken for get twin operations.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_send_duration_seconds.count,gauge,,,,Count of time taken to send a message.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_send_duration_seconds.sum,gauge,,,,Sum of time taken to send a message.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_send_duration_seconds.quantile,gauge,,,,Quantile of time taken to send a message.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_process_duration_seconds.count,gauge,,,,Count of time taken to process a message from the queue.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_process_duration_seconds.sum,gauge,,,,Sum of time taken to process a message from the queue.,0,azure_iot_edge, +azure.iot_edge.edge_hub.message_process_duration_seconds.quantile,gauge,,,,Quantile of time taken to process a message from the queue.,0,azure_iot_edge, +azure.iot_edge.edge_hub.reported_properties_update_duration_seconds.count,gauge,,,,Count of time taken to update reported properties.,0,azure_iot_edge, +azure.iot_edge.edge_hub.reported_properties_update_duration_seconds.sum,gauge,,,,Sum of time taken to update reported properties.,0,azure_iot_edge, +azure.iot_edge.edge_hub.reported_properties_update_duration_seconds.quantile,gauge,,,,Quantile of time taken to update reported properties.,0,azure_iot_edge, +azure.iot_edge.edge_hub.direct_method_duration_seconds.count,gauge,,,,Count of time taken to resolve a direct message.,0,azure_iot_edge, +azure.iot_edge.edge_hub.direct_method_duration_seconds.sum,gauge,,,,Sum of time taken to resolve a direct message.,0,azure_iot_edge, +azure.iot_edge.edge_hub.direct_method_duration_seconds.quantile,gauge,,,,Quantile of time taken to resolve a direct message.,0,azure_iot_edge, +azure.iot_edge.edge_hub.direct_methods_total,count,,,,Total number of direct messages sent.,0,azure_iot_edge, +azure.iot_edge.edge_hub.queue_length,gauge,,,,"Current length of Edge Hub's queue for a given `priority`.",0,azure_iot_edge, +azure.iot_edge.edge_hub.messages_dropped_total,count,,,,"Total number of messages removed because of `reason`.",0,azure_iot_edge, +azure.iot_edge.edge_hub.messages_unack_total,count,,,,Total number of messages unack because of storage failure.,0,azure_iot_edge, +azure.iot_edge.edge_hub.offline_count_total,count,,,,Total number of times Edge Hub went offline.,0,azure_iot_edge, +azure.iot_edge.edge_hub.offline_duration_seconds.count,gauge,,,,Count of time Edge Hub was offline.,0,azure_iot_edge, +azure.iot_edge.edge_hub.offline_duration_seconds.sum,gauge,,,,Sum of time Edge Hub was offline.,0,azure_iot_edge, +azure.iot_edge.edge_hub.offline_duration_seconds.quantile,gauge,,,,Quantile of time Edge Hub was offline.,0,azure_iot_edge, +azure.iot_edge.edge_hub.operation_retry_total,count,,,,Total number of times Edge operations were retried.,0,azure_iot_edge, +azure.iot_edge.edge_hub.client_connect_failed_total,count,,,,Total number of times clients failed to connect to Edge Hub.,0,azure_iot_edge, +azure.iot_edge.edge_agent.total_time_running_correctly_seconds,gauge,,,,"The amount of time the module `module_name` was specified in the deployment and was in the running state.",0,azure_iot_edge, +azure.iot_edge.edge_agent.total_time_expected_running_seconds,gauge,,,,"The amount of time the module `module_name` was specified in the deployment.",0,azure_iot_edge, +azure.iot_edge.edge_agent.module_start_total,count,,,,"Number of times the Edge Agent asked Docker to start the module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.module_stop_total,count,,,,"Number of times the Edge Agent asked Docker to stop the module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.command_latency_seconds.count,gauge,,,,"Count of how long it took for Docker to execute the given `command`. Possible commands are: create, update, remove, start, stop, restart.",0,azure_iot_edge, +azure.iot_edge.edge_agent.command_latency_seconds.sum,gauge,,,,"Sum of how long it took for Docker to execute the given `command`. Possible commands are: create, update, remove, start, stop, restart.",0,azure_iot_edge, +azure.iot_edge.edge_agent.command_latency_seconds.quantile,gauge,,,,"Quantile of how long it took for Docker to execute the given `command`. Possible commands are: create, update, remove, start, stop, restart.",0,azure_iot_edge, +azure.iot_edge.edge_agent.iothub_syncs_total,count,,,,"Total number of times the Edge Agent attempted to sync its twin with IoT Hub, both successful and unsuccessful. Includes both Edge Agent requesting a twin, and IoT Hub notifying of a twin update.",0,azure_iot_edge, +azure.iot_edge.edge_agent.unsuccessful_iothub_syncs_total,count,,,,Total number of times the Edge Agent failed to sync its twin with IoT Hub.,0,azure_iot_edge, +azure.iot_edge.edge_agent.deployment_time_seconds.count,gauge,,,,Count of amount of time it took to complete a new deployment after receiving a change.,0,azure_iot_edge, +azure.iot_edge.edge_agent.deployment_time_seconds.sum,gauge,,,,Sum of amount of time it took to complete a new deployment after receiving a change.,0,azure_iot_edge, +azure.iot_edge.edge_agent.deployment_time_seconds.quantile,gauge,,,,Quantile of amount of time it took to complete a new deployment after receiving a change.,0,azure_iot_edge, +azure.iot_edge.edge_agent.direct_method_invocations_count,count,,,,"Total number of times a built-in Edge Agent direct method is called, such as Ping or Restart.",0,azure_iot_edge, +azure.iot_edge.edge_agent.host_uptime_seconds,gauge,,,,"How long the host has been running.",0,azure_iot_edge, +azure.iot_edge.edge_agent.iotedged_uptime_seconds,gauge,,,,"How long `iotedged` has been running.",0,azure_iot_edge, +azure.iot_edge.edge_agent.available_disk_space_bytes,gauge,,,,"Amount of space left on the disk `disk_name.",0,azure_iot_edge, +azure.iot_edge.edge_agent.total_disk_space_bytes,gauge,,,,"Size of the disk `disk_name.",0,azure_iot_edge, +azure.iot_edge.edge_agent.used_memory_bytes,gauge,,,,"Amount of RAM used by all processes in module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.total_memory_bytes,gauge,,,,"Total amount of RAM available to module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.used_cpu_percent.count,gauge,,,,"Count of percent of CPU used by all processes in module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.used_cpu_percent.sum,gauge,,,,"Sum of percent of CPU used by all processes in module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.used_cpu_percent.quantile,gauge,,,,"Quantile of percent of CPU used by all processes in module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.created_pids_total,gauge,,,,"Total number of processes the module `module_name` has created.",0,azure_iot_edge, +azure.iot_edge.edge_agent.total_network_in_bytes,gauge,,,,"Total amount of bytes received from the network by module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.total_network_out_bytes,gauge,,,,"Total amount of bytes sent to the network by module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.total_disk_read_bytes,gauge,,,,"Total amount of bytes read from the disk by module `module_name`.",0,azure_iot_edge, +azure.iot_edge.edge_agent.total_disk_write_bytes,gauge,,,,"Total amount of bytes written to the disk by module `module_name`.",0,azure_iot_edge, diff --git a/azure_iot_edge/requirements-dev.txt b/azure_iot_edge/requirements-dev.txt new file mode 100644 index 0000000000000..98b5456bbd0e2 --- /dev/null +++ b/azure_iot_edge/requirements-dev.txt @@ -0,0 +1 @@ +-e ../datadog_checks_dev diff --git a/azure_iot_edge/requirements.in b/azure_iot_edge/requirements.in new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/azure_iot_edge/setup.py b/azure_iot_edge/setup.py new file mode 100644 index 0000000000000..69325531deea7 --- /dev/null +++ b/azure_iot_edge/setup.py @@ -0,0 +1,64 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from codecs import open # To use a consistent encoding +from os import path + +from setuptools import setup + +HERE = path.dirname(path.abspath(__file__)) + +# Get version info +ABOUT = {} +with open(path.join(HERE, 'datadog_checks', 'azure_iot_edge', '__about__.py')) as f: + exec(f.read(), ABOUT) + +# Get the long description from the README file +with open(path.join(HERE, 'README.md'), encoding='utf-8') as f: + long_description = f.read() + + +def get_dependencies(): + dep_file = path.join(HERE, 'requirements.in') + if not path.isfile(dep_file): + return [] + + with open(dep_file, encoding='utf-8') as f: + return f.readlines() + + +CHECKS_BASE_REQ = 'datadog-checks-base>=11.0.0' + + +setup( + name='datadog-azure_iot_edge', + version=ABOUT['__version__'], + description='The Azure IoT Edge check', + long_description=long_description, + long_description_content_type='text/markdown', + keywords='datadog agent azure_iot_edge check', + # The project's main homepage. + url='https://github.com/DataDog/integrations-core', + # Author details + author='Datadog', + author_email='packages@datadoghq.com', + # License + license='BSD-3-Clause', + # See https://pypi.org/classifiers + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'Topic :: System :: Monitoring', + 'License :: OSI Approved :: BSD License', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3.8', + ], + # The package we're going to ship + packages=['datadog_checks.azure_iot_edge'], + # Run-time dependencies + install_requires=[CHECKS_BASE_REQ], + extras_require={'deps': get_dependencies()}, + # Extra files to ship with the wheel package + include_package_data=True, +) diff --git a/azure_iot_edge/tests/README.md b/azure_iot_edge/tests/README.md new file mode 100644 index 0000000000000..c982733cfcd2d --- /dev/null +++ b/azure_iot_edge/tests/README.md @@ -0,0 +1,146 @@ +# Azure IoT Edge E2E Setup + +## Credits + +This test setup was adapted from: https://github.com/Azure/iotedgedev/tree/v2.1.4/docker/runtime + +## Building from RCs + +RCs for the Azure IoT Edge runtime are not available on standard package managers, so the build is done against GitHub release assets directly. + +See: https://docs.microsoft.com/en-us/azure/iot-edge/how-to-install-iot-edge-linux#install-runtime-using-release-assets + +## Use IoT Edge CLI + +Once the container is running, you can interact with the IoT Edge daemon like so: + +```bash +docker exec iot-edge-device iotedge -H http://:15580 +``` + +For example, to list the active IoT Edge modules: + +```bash +docker exec iot-edge-device iotedge -H "http://$(docker inspect iot-edge-device -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'):15580" list +``` + +## Simulated temperature sensor + +The `SimulatedTemperatureSensor` is enabled in the environment. + +To view simulated data, run: + +```bash +docker logs -f SimulatedTemperatureSensor +``` + +## Logs collection + +To send integration logs to Staging US, edit your `ddev` configuration with: + +```toml +[orgs.staging-us] +api_key = "" +logs_url = "" +``` + +Then start the environment using `-o staging-us`. + +Validate that logs are being sent by inspecting the logs section of `docker exec -it agent status`. + +## Generate mock server metrics + +The data in [`metrics/`](./compose/device/mock_server/metrics) was generated as follows: + +* Start an E2E environment. +* Let it run for a few minutes. +* Generate metrics files: + +```bash +curl localhost:9601/metrics > azure_iot_edge/tests/compose/mock_server/metrics/edge_hub.txt +curl localhost:9602/metrics > azure_iot_edge/tests/compose/mock_server/metrics/edge_agent.txt +``` + +* Manually edit `edge_agent.txt` (replace `` with the instance number from the output): + * Add a value line for `edgeAgent_unsuccessful_iothub_syncs_total` (no way to trigger unsuccessful syncs were found): + + ``` + edgeAgent_unsuccessful_iothub_syncs_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True"} 0 + ``` + + * Add value lines for `edgeAgent_module_stop_total`: + + ``` + edgeAgent_module_stop_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",module_version="1.0",ms_telemetry="True"} 0 + edgeAgent_module_stop_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",module_version="",ms_telemetry="True"} 0 + ``` + + * Add value lines for `edgeAgent_total_disk_space_bytes` (containers don't have individual disks): + + ``` + edgeAgent_total_disk_space_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 1073741824 + edgeAgent_total_disk_space_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 1073741824 + edgeAgent_total_disk_space_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 1073741824 + ``` + +* Manually edit `edge_hub.txt` (replace `` with the instance number from the output): + * Edit any `NaN` values so that all metrics report correctly. + * Add a value line for `edgehub_dropped_total`: + + ``` + edgehub_messages_dropped_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="",from="testEdgeDevice/SimulatedTemperatureSensor",from_route_output="temperatureOutput",reason="ttl_expiry",ms_telemetry="True"} 1 + ``` + + * Add a value line for `edgehub_offline_count_total`: + + ``` + edgehub_offline_count_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="",id="testEdgeDevice/$edgeHub",ms_telemetry="True"} 1 + ``` + + * Add a definition and value for the `edgehub_messages_unack_total` metric (no way to trigger storage failures locally was found): + + ``` + # HELP edgehub_messages_unack_total Total number of messages unack because storage failure + # TYPE edgehub_messages_unack_total counter + edgehub_messages_unack_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="",from="testEdgeDevice/SimulatedTemperatureSensor",from_route_output="temperatureOutput",reason="storage_failure",ms_telemetry="True"} 1 + ``` + + * Add a definition and value for the `edgehub_operation_retry_total` metric (no way to trigger them locally was found): + + ``` + # HELP edgehub_operation_retry_total Total number of times edgeHub operations were retried + # TYPE edgehub_operation_retry_total counter + edgehub_operation_retry_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="",id="testEdgeDevice/$edgeHub",operation="test",ms_telemetry="True"} 1 + ``` + + * Add a definition and value for the `edgehub_client_connect_failed_total` metric (we do not have auth set up yet): + + ``` + # HELP edgehub_client_connect_failed_total Total number of times clients failed to connect to edgeHub + # TYPE edgehub_client_connect_failed_total counter + edgehub_client_connect_failed_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",reason="not_authenticated",ms_telemetry="True"} 1 + ``` + +## Troubleshooting + +Guides: + +* https://docs.microsoft.com/en-us/azure/iot-edge/troubleshoot-common-errors +* https://docs.microsoft.com/en-us/azure/iot-edge/troubleshoot + +In particular, you can run a `check` command in the `iot-edge-device` container to check for any configuration or connectivity issues: + +```bash +$ docker exec -it iot-edge-device iotedge -H "http://$(docker inspect iot-edge-device -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'):15580" check +Configuration checks +-------------------- +√ config.yaml is well-formed - OK +√ config.yaml has well-formed connection string - OK +√ container engine is installed and functional - OK +× config.yaml has correct hostname - Error + config.yaml has hostname edgehub but device reports hostname 0504777ae8c2. + Hostname in config.yaml must either be identical to the device hostname or be a fully-qualified domain name that has the device hostname as the first component. +× config.yaml has correct URIs for daemon mgmt endpoint - Error + Error: could not execute list-modules request: an error occurred trying to connect: Connection refused (os error 111) +... +``` diff --git a/azure_iot_edge/tests/__init__.py b/azure_iot_edge/tests/__init__.py new file mode 100644 index 0000000000000..46dd167dcde48 --- /dev/null +++ b/azure_iot_edge/tests/__init__.py @@ -0,0 +1,3 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) diff --git a/azure_iot_edge/tests/common.py b/azure_iot_edge/tests/common.py new file mode 100644 index 0000000000000..31cabaa45db69 --- /dev/null +++ b/azure_iot_edge/tests/common.py @@ -0,0 +1,383 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +import os +from typing import List, Tuple + +from datadog_checks.base import is_affirmative +from datadog_checks.base.stubs.aggregator import AggregatorStub +from datadog_checks.dev import get_here + +HERE = get_here() + +IOT_EDGE_DEVICE_ID = 'testEdgeDevice' +IOT_EDGE_IOTHUB_HOSTNAME = 'iot-edge-dev-hub.azure-devices.net' + +MOCK_SERVER_PORT = 9678 +MOCK_EDGE_HUB_PROMETHEUS_URL = 'http://localhost:{}/metrics/edge_hub.txt'.format(MOCK_SERVER_PORT) +MOCK_EDGE_AGENT_PROMETHEUS_URL = 'http://localhost:{}/metrics/edge_agent.txt'.format(MOCK_SERVER_PORT) +# Defined in Edge Agent fixtures. +MOCK_EDGE_AGENT_VERSION = ('1', '0', '10', '1.0.10-rc2.34217022 (029016ef1bf82dec749161d95c6b73aa5ee9baf1)') + +CUSTOM_TAGS = ['env:testing'] + +TAGS = CUSTOM_TAGS + [ + 'edge_device:{}'.format(IOT_EDGE_DEVICE_ID), + 'iothub:{}'.format(IOT_EDGE_IOTHUB_HOSTNAME), +] + +HUB_METRICS = [ + ('azure.iot_edge.edge_hub.gettwin_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.messages_received_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.messages_sent_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.reported_properties_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.gettwin_duration_seconds.sum', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.gettwin_duration_seconds.count', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.gettwin_duration_seconds.quantile', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.message_send_duration_seconds.sum', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.message_send_duration_seconds.count', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.message_send_duration_seconds.quantile', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.message_process_duration_seconds.sum', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.message_process_duration_seconds.count', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.message_process_duration_seconds.quantile', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.reported_properties_update_duration_seconds.sum', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.reported_properties_update_duration_seconds.count', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.reported_properties_update_duration_seconds.quantile', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.direct_method_duration_seconds.sum', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.direct_method_duration_seconds.count', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.direct_method_duration_seconds.quantile', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.direct_methods_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.queue_length', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.messages_dropped_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.messages_unack_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.offline_count_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.offline_duration_seconds.sum', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.offline_duration_seconds.count', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.offline_duration_seconds.quantile', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_hub.operation_retry_total', AggregatorStub.MONOTONIC_COUNT), + ('azure.iot_edge.edge_hub.client_connect_failed_total', AggregatorStub.MONOTONIC_COUNT), +] # type: List[Tuple[str, int]] + +AGENT_METRICS = [ + ( + 'azure.iot_edge.edge_agent.iotedged_uptime_seconds', + AggregatorStub.GAUGE, + [], + ), + ( + 'azure.iot_edge.edge_agent.unsuccessful_iothub_syncs_total', + AggregatorStub.MONOTONIC_COUNT, + [], + ), + ( + 'azure.iot_edge.edge_agent.iothub_syncs_total', + AggregatorStub.MONOTONIC_COUNT, + [], + ), + ( + 'azure.iot_edge.edge_agent.host_uptime_seconds', + AggregatorStub.GAUGE, + [], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.sum', + AggregatorStub.GAUGE, + [], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.count', + AggregatorStub.GAUGE, + [], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.quantile', + AggregatorStub.GAUGE, + ['quantile:0.5'], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.quantile', + AggregatorStub.GAUGE, + ['quantile:0.9'], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.quantile', + AggregatorStub.GAUGE, + ['quantile:0.95'], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.quantile', + AggregatorStub.GAUGE, + ['quantile:0.99'], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.quantile', + AggregatorStub.GAUGE, + ['quantile:0.999'], + ), + ( + 'azure.iot_edge.edge_agent.deployment_time_seconds.quantile', + AggregatorStub.GAUGE, + ['quantile:0.9999'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.sum', + AggregatorStub.GAUGE, + ['module_name:host'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.count', + AggregatorStub.GAUGE, + ['module_name:host'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.quantile', + AggregatorStub.GAUGE, + ['module_name:host', 'quantile:0.5'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.quantile', + AggregatorStub.GAUGE, + ['module_name:host', 'quantile:0.9'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.quantile', + AggregatorStub.GAUGE, + ['module_name:host', 'quantile:0.95'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.quantile', + AggregatorStub.GAUGE, + ['module_name:host', 'quantile:0.99'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.quantile', + AggregatorStub.GAUGE, + ['module_name:host', 'quantile:0.999'], + ), + ( + 'azure.iot_edge.edge_agent.used_cpu_percent.quantile', + AggregatorStub.GAUGE, + ['module_name:host', 'quantile:0.9999'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.sum', + AggregatorStub.GAUGE, + ['command:wrap'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.count', + AggregatorStub.GAUGE, + ['command:wrap'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:wrap', 'quantile:0.5'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:wrap', 'quantile:0.9'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:wrap', 'quantile:0.95'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:wrap', 'quantile:0.99'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:wrap', 'quantile:0.999'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:wrap', 'quantile:0.9999'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.sum', + AggregatorStub.GAUGE, + ['command:create'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.count', + AggregatorStub.GAUGE, + ['command:create'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:create', 'quantile:0.5'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:create', 'quantile:0.9'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:create', 'quantile:0.95'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:create', 'quantile:0.99'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:create', 'quantile:0.999'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:create', 'quantile:0.9999'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.sum', + AggregatorStub.GAUGE, + ['command:start'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.count', + AggregatorStub.GAUGE, + ['command:start'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:start', 'quantile:0.5'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:start', 'quantile:0.9'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:start', 'quantile:0.95'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:start', 'quantile:0.99'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:start', 'quantile:0.999'], + ), + ( + 'azure.iot_edge.edge_agent.command_latency_seconds.quantile', + AggregatorStub.GAUGE, + ['command:start', 'quantile:0.9999'], + ), + ( + 'azure.iot_edge.edge_agent.module_start_total', + AggregatorStub.MONOTONIC_COUNT, + ['module_name:edgeHub', 'module_version:'], + ), + ( + 'azure.iot_edge.edge_agent.module_start_total', + AggregatorStub.MONOTONIC_COUNT, + ['module_name:SimulatedTemperatureSensor', 'module_version:1.0'], + ), + ( + 'azure.iot_edge.edge_agent.module_stop_total', + AggregatorStub.MONOTONIC_COUNT, + ['module_name:edgeHub', 'module_version:'], + ), + ( + 'azure.iot_edge.edge_agent.module_stop_total', + AggregatorStub.MONOTONIC_COUNT, + ['module_name:SimulatedTemperatureSensor', 'module_version:1.0'], + ), +] # type: List[Tuple[str, int, List[str]]] + +MODULES = [ + 'edgeHub', + 'edgeAgent', + 'SimulatedTemperatureSensor', +] + +MODULE_METRICS = [ + ( + 'azure.iot_edge.edge_agent.total_network_in_bytes', + AggregatorStub.GAUGE, + ), + ('azure.iot_edge.edge_agent.total_network_out_bytes', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.total_disk_read_bytes', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.total_disk_write_bytes', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.total_disk_space_bytes', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.created_pids_total', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.total_time_expected_running_seconds', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.total_time_running_correctly_seconds', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.used_memory_bytes', AggregatorStub.GAUGE), + ('azure.iot_edge.edge_agent.total_memory_bytes', AggregatorStub.GAUGE), +] # type: List[Tuple[str, int]] + +E2E_LIBIOTHSM_STD_URL = os.environ['IOT_EDGE_E2E_LIBIOTHSM_STD_URL'] +E2E_IOTEDGE_URL = os.environ['IOT_EDGE_E2E_IOTEDGE_URL'] +E2E_IMAGE = os.environ['IOT_EDGE_E2E_IMAGE'] +E2E_IOT_EDGE_TLS_ENABLED = is_affirmative(os.environ.get('IOT_EDGE_E2E_TLS_ENABLED')) + +# Obtained from: `az iot hub device-identity connection-string show --device-id --hub-name ` +E2E_IOT_EDGE_CONNSTR = os.environ.get('IOT_EDGE_CONNSTR', '') + +E2E_IOT_EDGE_ROOT_CA_CERT = os.path.join(HERE, 'tls', 'certs', 'azure-iot-test-only.root.ca.cert.pem') +E2E_IOT_EDGE_DEVICE_CA_CERT = os.path.join(HERE, 'tls', 'certs', 'new-device.cert.pem') +E2E_IOT_EDGE_DEVICE_CA_PK = os.path.join(HERE, 'tls', 'private', 'new-device.key.pem') + +E2E_NETWORK = 'iot-edge-network' # External, create it using `$ docker network create`. +E2E_EDGE_HUB_PROMETHEUS_URL = 'http://localhost:9601/metrics' +E2E_EDGE_AGENT_PROMETHEUS_URL = 'http://localhost:9602/metrics' +E2E_EXTRA_SPAWNED_CONTAINERS = [ + # Spawned by the Edge Agent after device has started. + 'edgeHub', + 'SimulatedTemperatureSensor', +] + +E2E_METRICS = ( + # All metrics... + {name for name, _ in MODULE_METRICS} + .union(name for name, _, _ in AGENT_METRICS) + .union(name for name, _ in HUB_METRICS) + # ... Except a few that don't get emitted by default. + .difference( + { + 'azure.iot_edge.edge_agent.module_stop_total', + 'azure.iot_edge.edge_agent.unsuccessful_iothub_syncs_total', + 'azure.iot_edge.edge_agent.total_disk_space_bytes', + 'azure.iot_edge.edge_hub.direct_methods_total', + 'azure.iot_edge.edge_hub.direct_method_duration_seconds.sum', + 'azure.iot_edge.edge_hub.direct_method_duration_seconds.count', + 'azure.iot_edge.edge_hub.direct_method_duration_seconds.quantile', + 'azure.iot_edge.edge_hub.messages_unack_total', + 'azure.iot_edge.edge_hub.messages_dropped_total', + 'azure.iot_edge.edge_hub.offline_count_total', + 'azure.iot_edge.edge_hub.operation_retry_total', + 'azure.iot_edge.edge_hub.client_connect_failed_total', + } + ) +) + +E2E_TAGS = CUSTOM_TAGS + [ + 'edge_device:{}'.format(IOT_EDGE_DEVICE_ID), + 'iothub:{}'.format(IOT_EDGE_IOTHUB_HOSTNAME), +] + +E2E_METADATA = { + 'env_vars': { + 'DD_LOGS_ENABLED': 'true', + }, + 'docker_volumes': [ + '/var/run/docker.sock:/var/run/docker.sock', + ], +} diff --git a/azure_iot_edge/tests/compose/device/Dockerfile b/azure_iot_edge/tests/compose/device/Dockerfile new file mode 100644 index 0000000000000..2b4af658931f1 --- /dev/null +++ b/azure_iot_edge/tests/compose/device/Dockerfile @@ -0,0 +1,39 @@ +FROM ubuntu:16.04 + +ARG LIBIOTHSM_STD_URL +ARG IOTEDGE_URL + +RUN apt-get update && apt-get install -y --no-install-recommends \ + apt-transport-https \ + ca-certificates \ + curl \ + iproute2 \ + iputils-ping \ + systemd && \ + rm -rf /var/lib/apt/lists/* + +RUN curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list > ./microsoft-prod.list && \ + cp ./microsoft-prod.list /etc/apt/sources.list.d/ && \ + curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg && \ + cp ./microsoft.gpg /etc/apt/trusted.gpg.d/ + +RUN apt-get update && apt-get install -y --no-install-recommends \ + moby-cli \ + moby-engine && \ + rm -rf /var/lib/apt/lists/* + +RUN env + +# Only GA versions are available in the Microsoft apt repository, so we need to install RCs from GitHub releases. +# See: https://github.com/MicrosoftDocs/azure-docs/issues/60354 +RUN curl -L "$LIBIOTHSM_STD_URL" -o libiothsm-std.deb && \ + dpkg -i ./libiothsm-std.deb +RUN curl -L "$IOTEDGE_URL" -o iotedge.deb && \ + dpkg -i ./iotedge.deb + +COPY rund.sh rund.sh + +RUN sed -i 's/\r//' ./rund.sh && \ + chmod u+x rund.sh + +ENTRYPOINT [ "./rund.sh" ] diff --git a/azure_iot_edge/tests/compose/device/rund.sh b/azure_iot_edge/tests/compose/device/rund.sh new file mode 100644 index 0000000000000..9760afa94dbe8 --- /dev/null +++ b/azure_iot_edge/tests/compose/device/rund.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +echo '=> detecting IP' +export IP=$(ip addr show eth0 | grep "inet\b" | awk '{print $2}' | cut -d/ -f1) +export IOT_EDGE_DEVICE_HOSTNAME="host.docker.internal" +ping -q -c1 $IOT_EDGE_DEVICE_HOSTNAME > /dev/null 2>&1 +if [ $? -ne 0 ]; then + IOT_EDGE_DEVICE_HOSTNAME=$(ip route | awk '/default/ { print $3 }' | awk '!seen[$0]++') +fi + +echo '=> creating config.yaml' +cat < /etc/iotedge/config.yaml +provisioning: + source: "manual" + device_connection_string: "$IOT_EDGE_DEVICE_CONNECTION_STRING" +agent: + name: "edgeAgent" + type: "docker" + env: {} + config: + image: "$IOT_EDGE_AGENT_IMAGE" + auth: {} +hostname: "edgehub" +connect: + # Use an HTTP endpoint, because mounting Unix sockets is not supported on Docker for macOS. + # See: https://github.com/docker/for-mac/issues/483 + management_uri: "http://$IOT_EDGE_DEVICE_HOSTNAME:15580" + workload_uri: "http://$IOT_EDGE_DEVICE_HOSTNAME:15581" +listen: + management_uri: "http://$IP:15580" + workload_uri: "http://$IP:15581" +homedir: "/var/lib/iotedge" +moby_runtime: + docker_uri: "/var/run/docker.sock" + network: "$IOT_EDGE_NETWORK" +EOF + +if [ $IOT_EDGE_TLS_ENABLED ]; then + cat <> /etc/iotedge/config.yaml +certificates: + device_ca_cert: $IOT_EDGE_DEVICE_CA_CERT + device_ca_pk: $IOT_EDGE_DEVICE_CA_PK + trusted_ca_certs: $IOT_EDGE_TRUSTED_CA_CERTS +EOF +fi + +cat /etc/iotedge/config.yaml + +echo '=> running iotedge daemon' +exec iotedged -c /etc/iotedge/config.yaml diff --git a/azure_iot_edge/tests/compose/docker-compose-tls.yaml b/azure_iot_edge/tests/compose/docker-compose-tls.yaml new file mode 100644 index 0000000000000..2972578688c95 --- /dev/null +++ b/azure_iot_edge/tests/compose/docker-compose-tls.yaml @@ -0,0 +1,35 @@ +version: "3" + +services: + iot-edge-device: + build: + context: ./device + args: + - LIBIOTHSM_STD_URL=${E2E_LIBIOTHSM_STD_URL} + - IOTEDGE_URL=${E2E_IOTEDGE_URL} + container_name: iot-edge-device + hostname: edgehub + ports: + - "15580:15580" # Required for IoT Edge inter-container communications. + - "15581:15581" # Required for IoT Edge inter-container communications. + environment: + - IOT_EDGE_AGENT_IMAGE=${E2E_IMAGE} + - IOT_EDGE_DEVICE_CONNECTION_STRING=${E2E_IOT_EDGE_CONNSTR} + - IOT_EDGE_NETWORK=iot-edge-network + - IOT_EDGE_TLS_ENABLED=true + - IOT_EDGE_TRUSTED_CA_CERTS=/private/root.cert.pem + - IOT_EDGE_DEVICE_CA_CERT=/private/device.cert.pem + - IOT_EDGE_DEVICE_CA_PK=/private/device.key.pem + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${E2E_IOT_EDGE_ROOT_CA_CERT}:/private/root.cert.pem + - ${E2E_IOT_EDGE_DEVICE_CA_CERT}:/private/device.cert.pem + - ${E2E_IOT_EDGE_DEVICE_CA_PK}:/private/device.key.pem + networks: + - iot-edge-network + labels: + com.datadoghq.ad.logs: '[{"source": "azure.iot_edge", "service": "azure_iot_edge_dev"}]' + +networks: + iot-edge-network: + external: true diff --git a/azure_iot_edge/tests/compose/docker-compose.yaml b/azure_iot_edge/tests/compose/docker-compose.yaml new file mode 100644 index 0000000000000..7830ea62d47b3 --- /dev/null +++ b/azure_iot_edge/tests/compose/docker-compose.yaml @@ -0,0 +1,28 @@ +version: "3" + +services: + iot-edge-device: + build: + context: ./device + args: + - LIBIOTHSM_STD_URL=${E2E_LIBIOTHSM_STD_URL} + - IOTEDGE_URL=${E2E_IOTEDGE_URL} + container_name: iot-edge-device + hostname: edgehub + ports: + - "15580:15580" # Required for IoT Edge inter-container communications. + - "15581:15581" # Required for IoT Edge inter-container communications. + environment: + - IOT_EDGE_AGENT_IMAGE=${E2E_IMAGE} + - IOT_EDGE_DEVICE_CONNECTION_STRING=${E2E_IOT_EDGE_CONNSTR} + - IOT_EDGE_NETWORK=iot-edge-network + volumes: + - /var/run/docker.sock:/var/run/docker.sock + networks: + - iot-edge-network + labels: + com.datadoghq.ad.logs: '[{"source": "azure.iot_edge", "service": "azure_iot_edge_dev"}]' + +networks: + iot-edge-network: + external: true diff --git a/azure_iot_edge/tests/compose/mock_server/data/metrics/edge_agent.txt b/azure_iot_edge/tests/compose/mock_server/data/metrics/edge_agent.txt new file mode 100644 index 0000000000000..370541455a9d1 --- /dev/null +++ b/azure_iot_edge/tests/compose/mock_server/data/metrics/edge_agent.txt @@ -0,0 +1,123 @@ +# HELP edgeAgent_used_cpu_percent Percent of cpu used by all processes +# TYPE edgeAgent_used_cpu_percent summary +edgeAgent_used_cpu_percent_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True"} 4.755497455596924 +edgeAgent_used_cpu_percent_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True"} 1 +edgeAgent_used_cpu_percent{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True",quantile="0.5"} 4.755497455596924 +edgeAgent_used_cpu_percent{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True",quantile="0.9"} 4.755497455596924 +edgeAgent_used_cpu_percent{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True",quantile="0.95"} 4.755497455596924 +edgeAgent_used_cpu_percent{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True",quantile="0.99"} 4.755497455596924 +edgeAgent_used_cpu_percent{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True",quantile="0.999"} 4.755497455596924 +edgeAgent_used_cpu_percent{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True",quantile="0.9999"} 4.755497455596924 +# HELP edgeAgent_total_disk_write_bytes The amount of bytes written to disk +# TYPE edgeAgent_total_disk_write_bytes gauge +edgeAgent_total_disk_write_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 0 +edgeAgent_total_disk_write_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 0 +edgeAgent_total_disk_write_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 0 +# HELP edgeAgent_used_memory_bytes Amount of RAM used by all processes +# TYPE edgeAgent_used_memory_bytes gauge +edgeAgent_used_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 51757056 +edgeAgent_used_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 120741888 +edgeAgent_used_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 23482368 +edgeAgent_used_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True"} 854896000 +# HELP edgeAgent_iotedged_uptime_seconds How long iotedged has been running +# TYPE edgeAgent_iotedged_uptime_seconds gauge +edgeAgent_iotedged_uptime_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True"} 68 +# HELP edgeAgent_total_disk_read_bytes The amount of bytes read from the disk +# TYPE edgeAgent_total_disk_read_bytes gauge +edgeAgent_total_disk_read_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 0 +edgeAgent_total_disk_read_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 0 +edgeAgent_total_disk_read_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 0 +# HELP edgeAgent_total_disk_space_bytes Size of the disk +# TYPE edgeAgent_total_disk_space_bytes gauge +edgeAgent_total_disk_space_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 1073741824 +edgeAgent_total_disk_space_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 1073741824 +edgeAgent_total_disk_space_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 1073741824 +# HELP edgeAgent_created_pids_total The number of processes or threads the container has created +# TYPE edgeAgent_created_pids_total gauge +edgeAgent_created_pids_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 19 +edgeAgent_created_pids_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 31 +edgeAgent_created_pids_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 14 +# HELP edgeAgent_total_time_expected_running_seconds The amount of time the module was specified in the deployment +# TYPE edgeAgent_total_time_expected_running_seconds gauge +edgeAgent_total_time_expected_running_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 128.385844 +edgeAgent_total_time_expected_running_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 121.1709888 +edgeAgent_total_time_expected_running_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 121.1710063 +# HELP edgeAgent_unsuccessful_iothub_syncs_total The amount of times edgeAgent failed to sync with iotHub +# TYPE edgeAgent_unsuccessful_iothub_syncs_total counter +edgeAgent_unsuccessful_iothub_syncs_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True"} 0 +# HELP edgeAgent_total_time_running_correctly_seconds The amount of time the module was specified in the deployment and was in the running state +# TYPE edgeAgent_total_time_running_correctly_seconds gauge +edgeAgent_total_time_running_correctly_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 128.385844 +edgeAgent_total_time_running_correctly_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 121.1709888 +edgeAgent_total_time_running_correctly_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 121.1710063 +# HELP edgeAgent_command_latency_seconds Command sent to module +# TYPE edgeAgent_command_latency_seconds summary +edgeAgent_command_latency_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True"} 6.69E-05 +edgeAgent_command_latency_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True"} 2 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True",quantile="0.5"} 9.9E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True",quantile="0.9"} 9.9E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True",quantile="0.95"} 9.9E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True",quantile="0.99"} 9.9E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True",quantile="0.999"} 9.9E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="wrap",ms_telemetry="True",quantile="0.9999"} 9.9E-06 +edgeAgent_command_latency_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True"} 0.00018889999999999998 +edgeAgent_command_latency_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True"} 2 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True",quantile="0.5"} 9.5E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True",quantile="0.9"} 9.5E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True",quantile="0.95"} 9.5E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True",quantile="0.99"} 9.5E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True",quantile="0.999"} 9.5E-06 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="start",ms_telemetry="True",quantile="0.9999"} 9.5E-06 +edgeAgent_command_latency_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True"} 0.005762300000000001 +edgeAgent_command_latency_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True"} 2 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True",quantile="0.5"} 0.0001628 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True",quantile="0.9"} 0.0001628 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True",quantile="0.95"} 0.0001628 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True",quantile="0.99"} 0.0001628 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True",quantile="0.999"} 0.0001628 +edgeAgent_command_latency_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",command="create",ms_telemetry="True",quantile="0.9999"} 0.0001628 +# HELP edgeAgent_iothub_syncs_total The amount of times edgeAgent attempted to sync with iotHub, both successful and unsuccessful +# TYPE edgeAgent_iothub_syncs_total counter +edgeAgent_iothub_syncs_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True"} 3 +# HELP edgeAgent_host_uptime_seconds How long the host has been on +# TYPE edgeAgent_host_uptime_seconds gauge +edgeAgent_host_uptime_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True"} 186906 +# HELP edgeAgent_total_memory_bytes RAM available +# TYPE edgeAgent_total_memory_bytes gauge +edgeAgent_total_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 6239551488 +edgeAgent_total_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 6239551488 +edgeAgent_total_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 6239551488 +edgeAgent_total_memory_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="host",ms_telemetry="True"} 6093312000 +# HELP edgeAgent_available_disk_space_bytes Amount of space left on the disk +# TYPE edgeAgent_available_disk_space_bytes gauge +# HELP edgeAgent_deployment_time_seconds The amount of time it took to complete a new deployment +# TYPE edgeAgent_deployment_time_seconds summary +edgeAgent_deployment_time_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True"} 1.6210579 +edgeAgent_deployment_time_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True"} 1 +edgeAgent_deployment_time_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True",quantile="0.5"} 1.6210579 +edgeAgent_deployment_time_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True",quantile="0.9"} 1.6210579 +edgeAgent_deployment_time_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True",quantile="0.95"} 1.6210579 +edgeAgent_deployment_time_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True",quantile="0.99"} 1.6210579 +edgeAgent_deployment_time_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True",quantile="0.999"} 1.6210579 +edgeAgent_deployment_time_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",ms_telemetry="True",quantile="0.9999"} 1.6210579 +# HELP edgeAgent_total_network_out_bytes The amount of bytes sent to network +# TYPE edgeAgent_total_network_out_bytes gauge +edgeAgent_total_network_out_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 1271886 +edgeAgent_total_network_out_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 125374 +edgeAgent_total_network_out_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 14988 +# HELP edgeAgent_total_network_in_bytes The amount of bytes recieved from the network +# TYPE edgeAgent_total_network_in_bytes gauge +edgeAgent_total_network_in_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeAgent",ms_telemetry="True"} 542908 +edgeAgent_total_network_in_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",ms_telemetry="True"} 107530 +edgeAgent_total_network_in_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",ms_telemetry="False"} 16517 +# HELP edgeAgent_module_start_total Command sent to module +# TYPE edgeAgent_module_start_total counter +edgeAgent_module_start_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",module_version="1.0",ms_telemetry="True"} 2 +edgeAgent_module_start_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",module_version="",ms_telemetry="True"} 2 +# HELP edgeAgent_metadata General metadata about the device. The value is always 0, information is encoded in the tags. +# TYPE edgeAgent_metadata gauge +edgeAgent_metadata{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",edge_agent_version="1.0.10-rc2.34217022 (029016ef1bf82dec749161d95c6b73aa5ee9baf1)",experimental_features="{\"Enabled\":true,\"DisableCloudSubscriptions\":false}",host_information="{\"OperatingSystemType\":\"linux\",\"Architecture\":\"x86_64\",\"Version\":\"1.0.10~rc2\",\"ServerVersion\":\"19.03.12\",\"KernelVersion\":\"4.19.76-linuxkit\",\"OperatingSystem\":\"Docker Desktop\",\"NumCpus\":4}",ms_telemetry="True"} 0 +# HELP edgeAgent_module_stop_total Command sent to module +# TYPE edgeAgent_module_stop_total counter +edgeAgent_module_stop_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="SimulatedTemperatureSensor",module_version="1.0",ms_telemetry="True"} 0 +edgeAgent_module_stop_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="c2c90030-2df4-4c92-98cb-deaa9ef05cac",module_name="edgeHub",module_version="",ms_telemetry="True"} 0 diff --git a/azure_iot_edge/tests/compose/mock_server/data/metrics/edge_hub.txt b/azure_iot_edge/tests/compose/mock_server/data/metrics/edge_hub.txt new file mode 100644 index 0000000000000..37e59692faec2 --- /dev/null +++ b/azure_iot_edge/tests/compose/mock_server/data/metrics/edge_hub.txt @@ -0,0 +1,121 @@ +# HELP edgehub_reported_properties_total Reported properties update calls +# TYPE edgehub_reported_properties_total counter +edgehub_reported_properties_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub",ms_telemetry="True"} 2 +# HELP edgehub_messages_sent_total Messages sent from edge hub +# TYPE edgehub_messages_sent_total counter +edgehub_messages_sent_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input="",priority="2000000000",ms_telemetry="True"} 216 +# HELP edgehub_gettwin_duration_seconds Time taken to get twin +# TYPE edgehub_gettwin_duration_seconds summary +edgehub_gettwin_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor"} 0.1908516 +edgehub_gettwin_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor"} 1 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.5"} 0.1908516 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.9"} 0.1908516 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.95"} 0.1908516 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.99"} 0.1908516 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.999"} 0.1908516 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.9999"} 0.1908516 +edgehub_gettwin_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor"} 0.2929041 +edgehub_gettwin_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor"} 1 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.5"} 0.2929041 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.9"} 0.2929041 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.95"} 0.2929041 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.99"} 0.2929041 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.999"} 0.2929041 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.9999"} 0.2929041 +edgehub_gettwin_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub"} 0.2356047 +edgehub_gettwin_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub"} 1 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub",quantile="0.5"} 0.2356047 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub",quantile="0.9"} 0.2356047 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub",quantile="0.95"} 0.2356047 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub",quantile="0.99"} 0.2356047 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub",quantile="0.999"} 0.2356047 +edgehub_gettwin_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub",quantile="0.9999"} 0.2356047 +# HELP edgehub_message_send_duration_seconds Time taken to send a message +# TYPE edgehub_message_send_duration_seconds summary +edgehub_message_send_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input=""} 23.03504240000001 +edgehub_message_send_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input=""} 216 +edgehub_message_send_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input="",quantile="0.5"} 0.1022345 +edgehub_message_send_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input="",quantile="0.9"} 0.1118193 +edgehub_message_send_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input="",quantile="0.95"} 0.1945529 +edgehub_message_send_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input="",quantile="0.99"} 0.2225961 +edgehub_message_send_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input="",quantile="0.999"} 0.2225961 +edgehub_message_send_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",from_route_output="temperatureOutput",to_route_input="",quantile="0.9999"} 0.2225961 +# HELP edgehub_offline_count_total EdgeHub offline count +# TYPE edgehub_offline_count_total counter +edgehub_offline_count_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True"} 1 +# HELP edgehub_messages_received_total Number of messages received from client +# TYPE edgehub_messages_received_total counter +edgehub_messages_received_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",route_output="temperatureOutput",ms_telemetry="True"} 216 +# HELP edgehub_offline_duration_seconds EdgeHub offline time +# TYPE edgehub_offline_duration_seconds summary +edgehub_offline_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True"} 1.163 +edgehub_offline_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True"} 1 +edgehub_offline_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True",quantile="0.5"} 1.163 +edgehub_offline_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True",quantile="0.9"} 1.163 +edgehub_offline_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True",quantile="0.95"} 1.163 +edgehub_offline_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True",quantile="0.99"} 1.163 +edgehub_offline_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True",quantile="0.999"} 1.163 +edgehub_offline_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",ms_telemetry="True",quantile="0.9999"} 1.163 +# HELP edgehub_message_size_bytes Size of messages received by EdgeHub +# TYPE edgehub_message_size_bytes summary +edgehub_message_size_bytes_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor"} 90872 +edgehub_message_size_bytes_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor"} 216 +edgehub_message_size_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.5"} 421 +edgehub_message_size_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.9"} 422 +edgehub_message_size_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.95"} 422 +edgehub_message_size_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.99"} 422 +edgehub_message_size_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.999"} 422 +edgehub_message_size_bytes{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",quantile="0.9999"} 422 +# HELP edgehub_direct_method_duration_seconds Time taken to call direct method +# TYPE edgehub_direct_method_duration_seconds summary +edgehub_direct_method_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub"} 0.0027822000000000003 +edgehub_direct_method_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub"} 3 +edgehub_direct_method_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub",quantile="0.5"} 0.0001832 +edgehub_direct_method_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub",quantile="0.9"} 0.0001832 +edgehub_direct_method_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub",quantile="0.95"} 0.0001832 +edgehub_direct_method_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub",quantile="0.99"} 0.0001832 +edgehub_direct_method_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub",quantile="0.999"} 0.0001832 +edgehub_direct_method_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub",quantile="0.9999"} 0.0001832 +# HELP edgehub_message_process_duration_seconds Time taken to process message in EdgeHub +# TYPE edgehub_message_process_duration_seconds summary +edgehub_message_process_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True"} 0.8970831 +edgehub_message_process_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True"} 216 +edgehub_message_process_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True",quantile="0.5"} 0.0010925 +edgehub_message_process_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True",quantile="0.9"} 0.0015201 +edgehub_message_process_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True",quantile="0.95"} 0.0017037 +edgehub_message_process_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True",quantile="0.99"} 0.0036118 +edgehub_message_process_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True",quantile="0.999"} 0.0036118 +edgehub_message_process_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",to="upstream",priority="2000000000",ms_telemetry="True",quantile="0.9999"} 0.0036118 +# HELP edgehub_gettwin_total Get twin calls +# TYPE edgehub_gettwin_total counter +edgehub_gettwin_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="edge_hub",id="testEdgeDevice/SimulatedTemperatureSensor",ms_telemetry="True"} 1 +edgehub_gettwin_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/$edgeHub",ms_telemetry="True"} 1 +edgehub_gettwin_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",source="upstream",id="testEdgeDevice/SimulatedTemperatureSensor",ms_telemetry="True"} 1 +# HELP edgehub_queue_length Number of messages pending to be processed for the endpoint +# TYPE edgehub_queue_length gauge +edgehub_queue_length{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",endpoint="iothub",priority="2000000000",ms_telemetry="True"} 0 +# HELP edgehub_messages_dropped_total Messages cleaned up because of TTL expired +# TYPE edgehub_messages_dropped_total counter +edgehub_messages_dropped_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",from_route_output="temperatureOutput",reason="ttl_expiry",ms_telemetry="True"} 1 +# HELP edgehub_direct_methods_total Direct methods routed through EdgeHub +# TYPE edgehub_direct_methods_total counter +edgehub_direct_methods_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="upstream",to="testEdgeDevice/$edgeHub",ms_telemetry="True"} 3 +# HELP edgehub_reported_properties_update_duration_seconds Time taken to update reported properties +# TYPE edgehub_reported_properties_update_duration_seconds summary +edgehub_reported_properties_update_duration_seconds_sum{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub"} 0.5784961 +edgehub_reported_properties_update_duration_seconds_count{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub"} 2 +edgehub_reported_properties_update_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub",quantile="0.5"} 0.5784961 +edgehub_reported_properties_update_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub",quantile="0.9"} 0.5784961 +edgehub_reported_properties_update_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub",quantile="0.95"} 0.5784961 +edgehub_reported_properties_update_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub",quantile="0.99"} 0.5784961 +edgehub_reported_properties_update_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub",quantile="0.999"} 0.5784961 +edgehub_reported_properties_update_duration_seconds{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",target="upstream",id="testEdgeDevice/$edgeHub",quantile="0.9999"} 0.5784961 +# HELP edgehub_messages_unack_total Total number of messages unack because storage failure +# TYPE edgehub_messages_unack_total counter +edgehub_messages_unack_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",from="testEdgeDevice/SimulatedTemperatureSensor",from_route_output="temperatureOutput",reason="storage_failure",ms_telemetry="True"} 1 +# HELP edgehub_operation_retry_total Total number of times edgeHub operations were retried +# TYPE edgehub_operation_retry_total counter +edgehub_operation_retry_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/$edgeHub",operation="test",ms_telemetry="True"} 1 +# HELP edgehub_client_connect_failed_total Total number of times clients failed to connect to edgeHub +# TYPE edgehub_client_connect_failed_total counter +edgehub_client_connect_failed_total{iothub="iot-edge-dev-hub.azure-devices.net",edge_device="testEdgeDevice",instance_number="0dab21d7-d0de-4527-99df-27c8e5861eac",id="testEdgeDevice/SimulatedTemperatureSensor",reason="not_authenticated",ms_telemetry="True"} 1 diff --git a/azure_iot_edge/tests/compose/mock_server/docker-compose-windows.yaml b/azure_iot_edge/tests/compose/mock_server/docker-compose-windows.yaml new file mode 100644 index 0000000000000..2298121c951b6 --- /dev/null +++ b/azure_iot_edge/tests/compose/mock_server/docker-compose-windows.yaml @@ -0,0 +1,10 @@ +version: "3" + +services: + iot-edge-mock: + image: caddy:2.1.1-windowsservercore + container_name: iot-edge-mock + volumes: + - ./data/:C:\usr\share\caddy\ + ports: + - ${MOCK_SERVER_PORT}:80 diff --git a/azure_iot_edge/tests/compose/mock_server/docker-compose.yaml b/azure_iot_edge/tests/compose/mock_server/docker-compose.yaml new file mode 100644 index 0000000000000..17c4cfbadf606 --- /dev/null +++ b/azure_iot_edge/tests/compose/mock_server/docker-compose.yaml @@ -0,0 +1,10 @@ +version: "3" + +services: + iot-edge-mock: + image: caddy:2.1.1-alpine + container_name: iot-edge-mock + volumes: + - ./data/:/usr/share/caddy/ + ports: + - ${MOCK_SERVER_PORT}:80 diff --git a/azure_iot_edge/tests/conftest.py b/azure_iot_edge/tests/conftest.py new file mode 100644 index 0000000000000..9bb7ff227555d --- /dev/null +++ b/azure_iot_edge/tests/conftest.py @@ -0,0 +1,104 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +import os + +import pytest + +from datadog_checks.azure_iot_edge.types import Instance +from datadog_checks.base.utils.platform import Platform +from datadog_checks.dev import docker_run +from datadog_checks.dev.conditions import CheckDockerLogs, WaitFor + +from . import common, e2e_utils + + +@pytest.fixture(scope='session') +def dd_environment(e2e_instance): + if not common.E2E_IOT_EDGE_CONNSTR: + raise RuntimeError("IOT_EDGE_CONNSTR must be set to start or stop the E2E environment.") + + if common.E2E_IOT_EDGE_TLS_ENABLED: + compose_filename = 'docker-compose-tls.yaml' + else: + compose_filename = 'docker-compose.yaml' + + compose_file = os.path.join(common.HERE, 'compose', compose_filename) + + conditions = [ + CheckDockerLogs(compose_file, r'[mgmt] .* 200 OK', wait=5), # Verify Security Manager boots. + CheckDockerLogs(compose_file, 'Successfully started module edgeAgent', wait=5), + CheckDockerLogs(compose_file, 'Successfully started module edgeHub', wait=5), + CheckDockerLogs(compose_file, 'Successfully started module SimulatedTemperatureSensor', wait=5), + WaitFor(e2e_utils.edge_hub_endpoint_ready), + WaitFor(e2e_utils.edge_agent_endpoint_ready), + ] + + env_vars = { + "E2E_LIBIOTHSM_STD_URL": common.E2E_LIBIOTHSM_STD_URL, + "E2E_IOTEDGE_URL": common.E2E_IOTEDGE_URL, + "E2E_IMAGE": common.E2E_IMAGE, + "E2E_IOT_EDGE_CONNSTR": common.E2E_IOT_EDGE_CONNSTR, + } + + if common.E2E_IOT_EDGE_TLS_ENABLED: + for path in ( + common.E2E_IOT_EDGE_DEVICE_CA_CERT, + common.E2E_IOT_EDGE_DEVICE_CA_CERT, + common.E2E_IOT_EDGE_DEVICE_CA_PK, + ): + if not os.path.exists(path): + message = ( + "Path {!r} does not exist. " + "Please follow instructions in azure_iot_edge/tests/tls/README.md to " + "configure test TLS certificates." + ).format(path) + raise RuntimeError(message) + + env_vars.update( + { + "E2E_IOT_EDGE_ROOT_CA_CERT": common.E2E_IOT_EDGE_ROOT_CA_CERT, + "E2E_IOT_EDGE_DEVICE_CA_CERT": common.E2E_IOT_EDGE_DEVICE_CA_CERT, + "E2E_IOT_EDGE_DEVICE_CA_PK": common.E2E_IOT_EDGE_DEVICE_CA_PK, + } + ) + + up = e2e_utils.IoTEdgeUp(compose_file, network_name=common.E2E_NETWORK) + down = e2e_utils.IoTEdgeDown(compose_file, stop_extra_containers=common.E2E_EXTRA_SPAWNED_CONTAINERS) + + with docker_run(conditions=conditions, env_vars=env_vars, up=up, down=down): + yield e2e_instance, common.E2E_METADATA + + +@pytest.fixture(scope='session') +def e2e_instance(): + # type: () -> Instance + return { + 'edge_hub_prometheus_url': common.E2E_EDGE_HUB_PROMETHEUS_URL, + 'edge_agent_prometheus_url': common.E2E_EDGE_AGENT_PROMETHEUS_URL, + 'tags': common.CUSTOM_TAGS, + } + + +@pytest.fixture(scope='session') +def mock_server(): + if Platform.is_windows(): + compose_filename = 'docker-compose-windows.yaml' + else: + compose_filename = 'docker-compose.yaml' + + compose_file = os.path.join(common.HERE, 'compose', 'mock_server', compose_filename) + env_vars = {"MOCK_SERVER_PORT": str(common.MOCK_SERVER_PORT)} + + with docker_run(compose_file, env_vars=env_vars): + yield + + +@pytest.fixture(scope='session') +def mock_instance(): + # type: () -> Instance + return { + 'edge_hub_prometheus_url': common.MOCK_EDGE_HUB_PROMETHEUS_URL, + 'edge_agent_prometheus_url': common.MOCK_EDGE_AGENT_PROMETHEUS_URL, + 'tags': common.CUSTOM_TAGS, + } diff --git a/azure_iot_edge/tests/e2e_utils.py b/azure_iot_edge/tests/e2e_utils.py new file mode 100644 index 0000000000000..8fde3c7ed29d1 --- /dev/null +++ b/azure_iot_edge/tests/e2e_utils.py @@ -0,0 +1,62 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from typing import Any + +import requests + +from datadog_checks.dev.docker import ComposeFileDown, ComposeFileUp +from datadog_checks.dev.structures import LazyFunction +from datadog_checks.dev.subprocess import run_command + +from . import common + + +class IoTEdgeUp(LazyFunction): + def __init__(self, compose_file, network_name): + # type: (str, str) -> None + self._compose_file_up = ComposeFileUp(compose_file) + self._network_name = network_name + + def __call__(self): + # type: () -> Any + result = run_command(['docker', 'network', 'inspect', self._network_name], capture=True) + network_exists = result.code == 0 + if not network_exists: + run_command(['docker', 'network', 'create', self._network_name], check=True) + + return self._compose_file_up() + + +class IoTEdgeDown(LazyFunction): + def __init__(self, compose_file, stop_extra_containers): + # type: (str, list) -> None + self._compose_file_down = ComposeFileDown(compose_file) + self._stop_extra_containers = stop_extra_containers + + def __call__(self): + # type: () -> Any + run_command(['docker', 'stop'] + self._stop_extra_containers, check=True) + return self._compose_file_down() + + +def edge_hub_endpoint_ready(): + # type: () -> bool + try: + response = requests.get(common.E2E_EDGE_HUB_PROMETHEUS_URL) + response.raise_for_status() + except requests.HTTPError: + return False + else: + return response.status_code == 200 + + +def edge_agent_endpoint_ready(): + # type: () -> bool + try: + response = requests.get(common.E2E_EDGE_AGENT_PROMETHEUS_URL) + response.raise_for_status() + except requests.HTTPError: + return False + else: + return response.status_code == 200 diff --git a/azure_iot_edge/tests/test_check.py b/azure_iot_edge/tests/test_check.py new file mode 100644 index 0000000000000..2b89dda24289d --- /dev/null +++ b/azure_iot_edge/tests/test_check.py @@ -0,0 +1,108 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +import copy + +import pytest +import requests + +from datadog_checks.azure_iot_edge import AzureIoTEdgeCheck +from datadog_checks.base.stubs.aggregator import AggregatorStub +from datadog_checks.base.stubs.datadog_agent import DatadogAgentStub +from datadog_checks.dev.utils import get_metadata_metrics + +from . import common + + +@pytest.mark.usefixtures("mock_server") +def test_check(aggregator, mock_instance): + # type: (AggregatorStub, dict) -> None + """ + Under normal conditions, metrics and service checks are collected as expected. + """ + check = AzureIoTEdgeCheck('azure_iot_edge', {}, [mock_instance]) + check.check(mock_instance) + + for metric, metric_type in common.HUB_METRICS: + # Don't assert exact tags since they're very complex (many cross products). + aggregator.assert_metric(metric, metric_type=metric_type) + m = aggregator._metrics[metric][0] + assert set(m.tags) >= set(common.TAGS) + + for metric, metric_type, metric_tags in common.AGENT_METRICS: + tags = common.TAGS + metric_tags + aggregator.assert_metric(metric, metric_type=metric_type, count=1, tags=tags) + + for metric, metric_type in common.MODULE_METRICS: + for module_name in common.MODULES: + tags = common.TAGS + ['module_name:{}'.format(module_name)] + aggregator.assert_metric(metric, metric_type=metric_type, count=1, tags=tags) + + aggregator.assert_service_check( + 'azure.iot_edge.edge_hub.prometheus.health', + AzureIoTEdgeCheck.OK, + count=1, + tags=common.CUSTOM_TAGS + ['endpoint:{}'.format(common.MOCK_EDGE_HUB_PROMETHEUS_URL)], + ) + aggregator.assert_service_check( + 'azure.iot_edge.edge_agent.prometheus.health', + AzureIoTEdgeCheck.OK, + count=1, + tags=common.CUSTOM_TAGS + ['endpoint:{}'.format(common.MOCK_EDGE_AGENT_PROMETHEUS_URL)], + ) + + aggregator.assert_all_metrics_covered() + aggregator.assert_metrics_using_metadata(get_metadata_metrics()) + + +@pytest.mark.usefixtures("mock_server") +def test_version_metadata(datadog_agent, mock_instance): + # type: (DatadogAgentStub, dict) -> None + check = AzureIoTEdgeCheck('azure_iot_edge', {}, [mock_instance]) + check.check_id = 'test:123' + check.run() + + major, minor, patch, raw = common.MOCK_EDGE_AGENT_VERSION + version_metadata = { + 'version.scheme': 'semver', + 'version.major': major, + 'version.minor': minor, + 'version.patch': patch, + 'version.raw': raw, + } + datadog_agent.assert_metadata('test:123', version_metadata) + + +@pytest.mark.usefixtures("mock_server") +@pytest.mark.parametrize( + "option, url, service_check", + [ + pytest.param( + "edge_agent_prometheus_url", + common.MOCK_EDGE_AGENT_PROMETHEUS_URL, + "azure.iot_edge.edge_agent.prometheus.health", + id="edge-agent", + ), + pytest.param( + "edge_hub_prometheus_url", + common.MOCK_EDGE_HUB_PROMETHEUS_URL, + "azure.iot_edge.edge_hub.prometheus.health", + id="edge-hub", + ), + ], +) +def test_prometheus_endpoint_down(aggregator, mock_instance, option, url, service_check): + # type: (AggregatorStub, dict, str, str, str) -> None + """ + When a Prometheus endpoint is unreachable, service check reports as CRITICAL. + """ + instance = copy.deepcopy(mock_instance) + wrong_port = common.MOCK_SERVER_PORT + 1 # Will trigger exception. + instance[option] = url.replace(str(common.MOCK_SERVER_PORT), str(wrong_port)) + + check = AzureIoTEdgeCheck('azure_iot_edge', {}, [instance]) + + with pytest.raises(requests.ConnectionError): + check.check(instance) + + aggregator.assert_service_check(service_check, AzureIoTEdgeCheck.CRITICAL) diff --git a/azure_iot_edge/tests/test_config.py b/azure_iot_edge/tests/test_config.py new file mode 100644 index 0000000000000..d6f99ccae686a --- /dev/null +++ b/azure_iot_edge/tests/test_config.py @@ -0,0 +1,83 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +import pytest + +from datadog_checks.azure_iot_edge.config import Config +from datadog_checks.azure_iot_edge.metrics import EDGE_AGENT_METRICS, EDGE_HUB_METRICS +from datadog_checks.azure_iot_edge.types import Instance +from datadog_checks.base import ConfigurationError + + +@pytest.mark.unit +def test_config(): + # type: () -> None + instance = { + 'edge_hub_prometheus_url': 'http://testserver:9601/metrics', + 'edge_agent_prometheus_url': 'http://testserver:9602/metrics', + } # type: Instance + + config = Config(instance) + + assert config.prometheus_instances == [ + { + 'prometheus_url': 'http://testserver:9601/metrics', + 'namespace': 'edge_hub', + 'metrics': EDGE_HUB_METRICS, + 'tags': [], + 'exclude_labels': ['ms_telemetry', 'instance_number'], + }, + { + 'prometheus_url': 'http://testserver:9602/metrics', + 'namespace': 'edge_agent', + 'metrics': EDGE_AGENT_METRICS, + 'tags': [], + 'exclude_labels': ['ms_telemetry', 'instance_number'], + 'metadata_metric_name': 'edgeAgent_metadata', + 'metadata_label_map': {'version': 'edge_agent_version'}, + }, + ] + + +@pytest.mark.unit +def test_config_custom_tags(): + # type: () -> None + tags = ['env:testing'] + instance = { + 'edge_hub_prometheus_url': '...', + 'edge_agent_prometheus_url': '...', + 'tags': tags, + } # type: Instance + + config = Config(instance) + + for instance in config.prometheus_instances: + assert instance['tags'] == tags + + +@pytest.mark.unit +@pytest.mark.parametrize('key', ['edge_hub_prometheus_url', 'edge_agent_prometheus_url']) +def test_config_required_options(key): + # type: (str) -> None + instance = { + 'edge_hub_prometheus_url': '...', + 'edge_agent_prometheus_url': '...', + } # type: Instance + + instance.pop(key) # type: ignore + + with pytest.raises(ConfigurationError): + _ = Config(instance) + + +@pytest.mark.unit +def test_config_tags_must_be_list(): + # type: () -> None + instance = { + 'edge_hub_prometheus_url': '...', + 'edge_agent_prometheus_url': '...', + 'tags': 'string:tags', # type: ignore + } # type: Instance + + with pytest.raises(ConfigurationError): + _ = Config(instance) diff --git a/azure_iot_edge/tests/test_e2e.py b/azure_iot_edge/tests/test_e2e.py new file mode 100644 index 0000000000000..684c3a7fa7c6f --- /dev/null +++ b/azure_iot_edge/tests/test_e2e.py @@ -0,0 +1,26 @@ +# (C) Datadog, Inc. 2020-present +# All rights reserved +# Licensed under a 3-clause BSD style license (see LICENSE) +from typing import Callable + +import pytest + +from datadog_checks.azure_iot_edge import AzureIoTEdgeCheck +from datadog_checks.base.stubs.aggregator import AggregatorStub + +from . import common + + +@pytest.mark.e2e +def test_e2e(dd_agent_check): + # type: (Callable) -> None + aggregator = dd_agent_check(rate=True) # type: AggregatorStub + + for metric in common.E2E_METRICS: + aggregator.assert_metric(metric) + m = aggregator._metrics[metric][0] + assert set(m.tags) >= set(common.E2E_TAGS) + + aggregator.assert_all_metrics_covered() + aggregator.assert_service_check('azure.iot_edge.edge_agent.prometheus.health', AzureIoTEdgeCheck.OK) + aggregator.assert_service_check('azure.iot_edge.edge_hub.prometheus.health', AzureIoTEdgeCheck.OK) diff --git a/azure_iot_edge/tests/tls/.gitignore b/azure_iot_edge/tests/tls/.gitignore new file mode 100644 index 0000000000000..fd4c81895c420 --- /dev/null +++ b/azure_iot_edge/tests/tls/.gitignore @@ -0,0 +1,9 @@ +# All files generated during test E2E TLS certs generation are private, and expire after 30 days. +# They should all be ignored. +certs/ +csr/ +intermediateCerts/ +newcerts/ +private/ +index* +serial* diff --git a/azure_iot_edge/tests/tls/README.md b/azure_iot_edge/tests/tls/README.md new file mode 100644 index 0000000000000..b2504bbbdd1ad --- /dev/null +++ b/azure_iot_edge/tests/tls/README.md @@ -0,0 +1,31 @@ +# azure_iot_edge certs + +Files in this directory were created following the [Create demo certificates to test IoT Edge device features](https://docs.microsoft.com/en-us/azure/iot-edge/how-to-create-test-certificates) guide. + +They aim at testing the integration works well with devices that are provisioned using X.509 certificates (such that communication between IoT Edge components is encrypted using TLS). + +## Generate E2E test TLS certificates + +To regenerate certificates for your own E2E testing, run the setup script: + +```bash +./azure_iot_edge/tests/tls/setup.sh +``` + +This script will prompt you to: + +* Upload a test root CA to the IoT Hub web UI. (Note that these certs expire after 30 days. After this period of time, you would need to go through this procedure again to do E2E testing.) +* Generate a verification code in the IoT Hub web UI, and enter it in the CLI. +* Upload the generated verification cert to IoT Hub web UI. +* Eventually the cert should show as "Verified" in the IoT Hub web UI. +* Make sure not to modify any filename for the generated files, since the `-tls` E2E environments rely on them. + +## Misc + +The following files were taken from https://github.com/Azure/azure-iot-sdk-c/tree/master/tools/CACertificates: + +``` +certGen.ssh +openssl_root_ca.cnf +openssl_device_intermediate_ca.cnf +``` diff --git a/azure_iot_edge/tests/tls/certGen.sh b/azure_iot_edge/tests/tls/certGen.sh new file mode 100755 index 0000000000000..4303739312736 --- /dev/null +++ b/azure_iot_edge/tests/tls/certGen.sh @@ -0,0 +1,394 @@ +#!/bin/bash + +## Copyright (c) Microsoft. All rights reserved. +## Licensed under the MIT license. See LICENSE file in the project root for full license information. + +############################################################################### +# This script demonstrates creating X.509 certificates for an Azure IoT Hub +# CA Cert deployment. +# +# These certs MUST NOT be used in production. It is expected that production +# certificates will be created using a company's proper secure signing process. +# These certs are intended only to help demonstrate and prototype CA certs. +############################################################################### + +root_ca_dir="." +home_dir="." +algorithm="genrsa" +COUNTRY="US" +STATE="WA" +LOCALITY="Redmond" +ORGANIZATION_NAME="My Organization" +root_ca_password="1234" +key_bits_length="4096" +days_till_expire=30 +ca_chain_prefix="azure-iot-test-only.chain.ca" +intermediate_ca_dir="." +openssl_root_config_file="./openssl_root_ca.cnf" +openssl_intermediate_config_file="./openssl_device_intermediate_ca.cnf" +intermediate_ca_password="1234" +root_ca_prefix="azure-iot-test-only.root.ca" +intermediate_ca_prefix="azure-iot-test-only.intermediate" + +function makeCNsubject() +{ + local result="/CN=${1}" + case $OSTYPE in + msys|win32) result="/${result}" + esac + echo "$result" +} + +function warn_certs_not_for_production() +{ + tput smso + tput setaf 3 + echo "Certs generated by this script are not for production (e.g. they have hard-coded passwords of '1234'." + echo "This script is only to help you understand Azure IoT Hub CA Certificates." + echo "Use your official, secure mechanisms for this cert generation." + echo "Also note that these certs will expire in ${days_till_expire} days." + tput sgr0 +} + +function generate_root_ca() +{ + local common_name="Azure IoT Hub CA Cert Test Only" + local password_cmd=" -aes256 -passout pass:${root_ca_password} " + + cd ${home_dir} + echo "Creating the Root CA Private Key" + + openssl ${algorithm} \ + ${password_cmd} \ + -out ${root_ca_dir}/private/${root_ca_prefix}.key.pem \ + ${key_bits_length} + [ $? -eq 0 ] || exit $? + chmod 400 ${root_ca_dir}/private/${root_ca_prefix}.key.pem + [ $? -eq 0 ] || exit $? + + echo "Creating the Root CA Certificate" + password_cmd=" -passin pass:${root_ca_password} " + + openssl req \ + -new \ + -x509 \ + -config ${openssl_root_config_file} \ + ${password_cmd} \ + -key ${root_ca_dir}/private/${root_ca_prefix}.key.pem \ + -subj "$(makeCNsubject "${common_name}")" \ + -days ${days_till_expire} \ + -sha256 \ + -extensions v3_ca \ + -out ${root_ca_dir}/certs/${root_ca_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + chmod 444 ${root_ca_dir}/certs/${root_ca_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + + echo "CA Root Certificate Generated At:" + echo "---------------------------------" + echo " ${root_ca_dir}/certs/${root_ca_prefix}.cert.pem" + echo "" + openssl x509 -noout -text \ + -in ${root_ca_dir}/certs/${root_ca_prefix}.cert.pem + + warn_certs_not_for_production + + [ $? -eq 0 ] || exit $? +} + + + +############################################################################### +# Generate Intermediate CA Cert +############################################################################### +function generate_intermediate_ca() +{ + local common_name="Azure IoT Hub Intermediate Cert Test Only" + + local password_cmd=" -aes256 -passout pass:${intermediate_ca_password} " + echo "Creating the Intermediate Device CA" + echo "-----------------------------------" + cd ${home_dir} + + openssl ${algorithm} \ + ${password_cmd} \ + -out ${intermediate_ca_dir}/private/${intermediate_ca_prefix}.key.pem \ + ${key_bits_length} + [ $? -eq 0 ] || exit $? + chmod 400 ${intermediate_ca_dir}/private/${intermediate_ca_prefix}.key.pem + [ $? -eq 0 ] || exit $? + + + echo "Creating the Intermediate Device CA CSR" + echo "-----------------------------------" + password_cmd=" -passin pass:${intermediate_ca_password} " + + openssl req -new -sha256 \ + ${password_cmd} \ + -config ${openssl_intermediate_config_file} \ + -subj "$(makeCNsubject "${common_name}")" \ + -key ${intermediate_ca_dir}/private/${intermediate_ca_prefix}.key.pem \ + -out ${intermediate_ca_dir}/csr/${intermediate_ca_prefix}.csr.pem + [ $? -eq 0 ] || exit $? + + echo "Signing the Intermediate Certificate with Root CA Cert" + echo "-----------------------------------" + password_cmd=" -passin pass:${root_ca_password} " + + openssl ca -batch \ + -config ${openssl_root_config_file} \ + ${password_cmd} \ + -extensions v3_intermediate_ca \ + -days ${days_till_expire} -notext -md sha256 \ + -in ${intermediate_ca_dir}/csr/${intermediate_ca_prefix}.csr.pem \ + -out ${intermediate_ca_dir}/certs/${intermediate_ca_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + chmod 444 ${intermediate_ca_dir}/certs/${intermediate_ca_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + + echo "Verify signature of the Intermediate Device Certificate with Root CA" + echo "-----------------------------------" + openssl verify \ + -CAfile ${root_ca_dir}/certs/${root_ca_prefix}.cert.pem \ + ${intermediate_ca_dir}/certs/${intermediate_ca_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + + echo "Intermediate CA Certificate Generated At:" + echo "-----------------------------------------" + echo " ${intermediate_ca_dir}/certs/${intermediate_ca_prefix}.cert.pem" + echo "" + openssl x509 -noout -text \ + -in ${intermediate_ca_dir}/certs/${intermediate_ca_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + + echo "Create Root + Intermediate CA Chain Certificate" + echo "-----------------------------------" + cat ${intermediate_ca_dir}/certs/${intermediate_ca_prefix}.cert.pem \ + ${root_ca_dir}/certs/${root_ca_prefix}.cert.pem > \ + ${intermediate_ca_dir}/certs/${ca_chain_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + chmod 444 ${intermediate_ca_dir}/certs/${ca_chain_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + + echo "Root + Intermediate CA Chain Certificate Generated At:" + echo "------------------------------------------------------" + echo " ${intermediate_ca_dir}/certs/${ca_chain_prefix}.cert.pem" + + warn_certs_not_for_production +} + +############################################################################### +# Generate a Certificate for a device using specific openssl extension and +# signed with either the root or intermediate cert. +############################################################################### +function generate_device_certificate_common() +{ + local common_name="${1}" + local device_prefix="${2}" + local certificate_dir="${3}" + local ca_password="${4}" + local server_pfx_password="1234" + local password_cmd=" -passin pass:${ca_password} " + local openssl_config_file="${5}" + local openssl_config_extension="${6}" + local cert_type_diagnostic="${7}" + + echo "Creating ${cert_type_diagnostic} Certificate" + echo "----------------------------------------" + cd ${home_dir} + + openssl ${algorithm} \ + -out ${certificate_dir}/private/${device_prefix}.key.pem \ + ${key_bits_length} + [ $? -eq 0 ] || exit $? + chmod 444 ${certificate_dir}/private/${device_prefix}.key.pem + [ $? -eq 0 ] || exit $? + + echo "Create the ${cert_type_diagnostic} Certificate Request" + echo "----------------------------------------" + openssl req -config ${openssl_config_file} \ + -key ${certificate_dir}/private/${device_prefix}.key.pem \ + -subj "$(makeCNsubject "${common_name}")" \ + -new -sha256 -out ${certificate_dir}/csr/${device_prefix}.csr.pem + [ $? -eq 0 ] || exit $? + + openssl ca -batch -config ${openssl_config_file} \ + ${password_cmd} \ + -extensions "${openssl_config_extension}" \ + -days ${days_till_expire} -notext -md sha256 \ + -in ${certificate_dir}/csr/${device_prefix}.csr.pem \ + -out ${certificate_dir}/certs/${device_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + chmod 444 ${certificate_dir}/certs/${device_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + + echo "Verify signature of the ${cert_type_diagnostic}" \ + " certificate with the signer" + echo "-----------------------------------" + openssl verify \ + -CAfile ${certificate_dir}/certs/${ca_chain_prefix}.cert.pem \ + ${certificate_dir}/certs/${device_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + + echo "${cert_type_diagnostic} Certificate Generated At:" + echo "----------------------------------------" + echo " ${certificate_dir}/certs/${device_prefix}.cert.pem" + echo "" + openssl x509 -noout -text \ + -in ${certificate_dir}/certs/${device_prefix}.cert.pem + [ $? -eq 0 ] || exit $? + echo "Create the ${cert_type_diagnostic} PFX Certificate" + echo "----------------------------------------" + openssl pkcs12 -in ${certificate_dir}/certs/${device_prefix}.cert.pem \ + -inkey ${certificate_dir}/private/${device_prefix}.key.pem \ + -password pass:${server_pfx_password} \ + -export -out ${certificate_dir}/certs/${device_prefix}.cert.pfx + [ $? -eq 0 ] || exit $? + echo "${cert_type_diagnostic} PFX Certificate Generated At:" + echo "--------------------------------------------" + echo " ${certificate_dir}/certs/${device_prefix}.cert.pfx" + [ $? -eq 0 ] || exit $? +} + +############################################################################### +# Generate a certificate for a leaf device +# signed with either the root or intermediate cert. +############################################################################### +function generate_leaf_certificate() +{ + local common_name="${1}" + local device_prefix="${2}" + local certificate_dir="${3}" + local ca_password="${4}" + local openssl_config_file="${5}" + + generate_device_certificate_common "${common_name}" "${device_prefix}" \ + "${certificate_dir}" "${ca_password}" \ + "${openssl_config_file}" "usr_cert" \ + "Leaf Device" +} + +############################################################################### +# Creates required directories and removes left over cert files. +# Run prior to creating Root CA; after that these files need to persist. +############################################################################### +function prepare_filesystem() +{ + if [ ! -f ${openssl_root_config_file} ]; then + echo "Missing file ${openssl_root_config_file}" + exit 1 + fi + + if [ ! -f ${openssl_intermediate_config_file} ]; then + echo "Missing file ${openssl_intermediate_config_file}" + exit 1 + fi + + rm -rf csr + rm -rf private + rm -rf certs + rm -rf intermediateCerts + rm -rf newcerts + + mkdir -p csr + mkdir -p private + mkdir -p certs + mkdir -p intermediateCerts + mkdir -p newcerts + + rm -f ./index.txt + touch ./index.txt + + rm -f ./serial + echo 01 > ./serial +} + +############################################################################### +# Generates a root and intermediate certificate for CA certs. +############################################################################### +function initial_cert_generation() +{ + prepare_filesystem + generate_root_ca + generate_intermediate_ca +} + +############################################################################### +# Generates a certificate for verification, chained directly to the root. +############################################################################### +function generate_verification_certificate() +{ + if [ $# -ne 1 ]; then + echo "Usage: " + exit 1 + fi + + rm -f ./private/verification-code.key.pem + rm -f ./certs/verification-code.cert.pem + generate_leaf_certificate "${1}" "verification-code" \ + ${root_ca_dir} ${root_ca_password} \ + ${openssl_root_config_file} +} + +############################################################################### +# Generates a certificate for a device, chained directly to the root. +############################################################################### +function generate_device_certificate() +{ + if [ $# -ne 1 ]; then + echo "Usage: " + exit 1 + fi + + rm -f ./private/new-device.key.pem + rm -f ./certs/new-device.key.pem + rm -f ./certs/new-device-full-chain.cert.pem + generate_leaf_certificate "${1}" "new-device" \ + ${root_ca_dir} ${root_ca_password} \ + ${openssl_root_config_file} +} + +############################################################################### +# Generates a certificate for a Edge device, chained to the intermediate. +############################################################################### +function generate_edge_device_certificate() +{ + local device_prefix="new-edge-device" + if [ $# -ne 1 ]; then + echo "Usage: " + exit 1 + fi + rm -f ./private/new-edge-device.key.pem + rm -f ./certs/new-edge-device.cert.pem + rm -f ./certs/new-edge-device-full-chain.cert.pem + + # Note: Appending a '.ca' to the common name is useful in situations + # where a user names their hostname as the edge device name. + # By doing so we avoid TLS validation errors where we have a server or + # client certificate where the hostname is used as the common name + # which essentially results in "loop" for validation purposes. + generate_device_certificate_common "${1}.ca" \ + ${device_prefix} \ + ${intermediate_ca_dir} \ + ${intermediate_ca_password} \ + ${openssl_intermediate_config_file} \ + "v3_intermediate_ca" "Edge Device" +} + +if [ "${1}" == "create_root_and_intermediate" ]; then + initial_cert_generation +elif [ "${1}" == "create_verification_certificate" ]; then + generate_verification_certificate "${2}" +elif [ "${1}" == "create_device_certificate" ]; then + generate_device_certificate "${2}" +elif [ "${1}" == "create_edge_device_certificate" ]; then + generate_edge_device_certificate "${2}" +else + echo "Usage: create_root_and_intermediate # Creates a new root and intermediate certificates" + echo " create_verification_certificate # Creates a verification certificate, signed with " + echo " create_device_certificate # Creates a device certificate, signed with " + echo " create_edge_device_certificate # Creates an edge device certificate, signed with " + exit 1 +fi + +warn_certs_not_for_production diff --git a/azure_iot_edge/tests/tls/openssl_device_intermediate_ca.cnf b/azure_iot_edge/tests/tls/openssl_device_intermediate_ca.cnf new file mode 100644 index 0000000000000..070e7098ac918 --- /dev/null +++ b/azure_iot_edge/tests/tls/openssl_device_intermediate_ca.cnf @@ -0,0 +1,127 @@ +# OpenSSL root CA configuration file. + +[ ca ] +default_ca = CA_default + +[ CA_default ] +# Directory and file locations. +dir = . +certs = $dir/certs +crl_dir = $dir/crl +new_certs_dir = $dir/newcerts +database = $dir/index.txt +serial = $dir/serial +RANDFILE = $dir/private/.rand + +# The root key and root certificate. +private_key = $dir/private/azure-iot-test-only.intermediate.key.pem +certificate = $dir/certs/azure-iot-test-only.intermediate.cert.pem + +# For certificate revocation lists. +crlnumber = $dir/crlnumber +crl = $dir/crl/azure-iot-test-only.intermediate.crl.pem +crl_extensions = crl_ext +default_crl_days = 30 + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +name_opt = ca_default +cert_opt = ca_default +default_days = 375 +preserve = no +policy = policy_loose + +[ policy_strict ] +# The root CA should only sign intermediate certificates that match. +countryName = optional +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ policy_loose ] +# Allow the intermediate CA to sign a more diverse range of certificates. +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ req ] +default_bits = 2048 +distinguished_name = req_distinguished_name +string_mask = utf8only + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +# Extension to add when the -x509 option is used. +x509_extensions = v3_ca + +[ req_distinguished_name ] +# See . +countryName = Country Name (2 letter code) +stateOrProvinceName = State or Province Name +localityName = Locality Name +0.organizationName = Organization Name +organizationalUnitName = Organizational Unit Name +commonName = Common Name +emailAddress = Email Address + +# Optionally, specify some defaults. +countryName_default = US +stateOrProvinceName_default = WA +localityName_default = +0.organizationName_default = My Organization +organizationalUnitName_default = +emailAddress_default = + +[ v3_ca ] +# Extensions for a typical CA. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ v3_intermediate_ca ] +# Extensions for a typical intermediate CA. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ usr_cert ] +# Extensions for client certificates. +basicConstraints = CA:FALSE +nsCertType = client, email +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection + +[ server_cert ] +# Extensions for server certificates. +basicConstraints = CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth + +[ crl_ext ] +# Extension for CRLs. +authorityKeyIdentifier=keyid:always + +[ ocsp ] +# Extension for OCSP signing certificates. +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, digitalSignature +extendedKeyUsage = critical, OCSPSigning diff --git a/azure_iot_edge/tests/tls/openssl_root_ca.cnf b/azure_iot_edge/tests/tls/openssl_root_ca.cnf new file mode 100644 index 0000000000000..08291325d15f2 --- /dev/null +++ b/azure_iot_edge/tests/tls/openssl_root_ca.cnf @@ -0,0 +1,127 @@ +# OpenSSL root CA configuration file. + +[ ca ] +default_ca = CA_default + +[ CA_default ] +# Directory and file locations. +dir = . +certs = $dir/certs +crl_dir = $dir/crl +new_certs_dir = $dir/newcerts +database = $dir/index.txt +serial = $dir/serial +RANDFILE = $dir/private/.rand + +# The root key and root certificate. +private_key = $dir/private/azure-iot-test-only.root.ca.key.pem +certificate = $dir/certs/azure-iot-test-only.root.ca.cert.pem + +# For certificate revocation lists. +crlnumber = $dir/crlnumber +crl = $dir/crl/azure-iot-test-only.intermediate.crl.pem +crl_extensions = crl_ext +default_crl_days = 30 + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +name_opt = ca_default +cert_opt = ca_default +default_days = 375 +preserve = no +policy = policy_loose + +[ policy_strict ] +# The root CA should only sign intermediate certificates that match. +countryName = optional +stateOrProvinceName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ policy_loose ] +# Allow the intermediate CA to sign a more diverse range of certificates. +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ req ] +default_bits = 2048 +distinguished_name = req_distinguished_name +string_mask = utf8only + +# SHA-1 is deprecated, so use SHA-2 instead. +default_md = sha256 + +# Extension to add when the -x509 option is used. +x509_extensions = v3_ca + +[ req_distinguished_name ] +# See . +countryName = Country Name (2 letter code) +stateOrProvinceName = State or Province Name +localityName = Locality Name +0.organizationName = Organization Name +organizationalUnitName = Organizational Unit Name +commonName = Common Name +emailAddress = Email Address + +# Optionally, specify some defaults. +countryName_default = US +stateOrProvinceName_default = WA +localityName_default = +0.organizationName_default = My Organization +organizationalUnitName_default = +emailAddress_default = + +[ v3_ca ] +# Extensions for a typical CA. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ v3_intermediate_ca ] +# Extensions for a typical intermediate CA. +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign + +[ usr_cert ] +# Extensions for client certificates. +basicConstraints = CA:FALSE +nsCertType = client, email +nsComment = "OpenSSL Generated Client Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment +extendedKeyUsage = clientAuth, emailProtection + +[ server_cert ] +# Extensions for server certificates. +basicConstraints = CA:FALSE +nsCertType = server +nsComment = "OpenSSL Generated Server Certificate" +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer:always +keyUsage = critical, digitalSignature, keyEncipherment +extendedKeyUsage = serverAuth + +[ crl_ext ] +# Extension for CRLs. +authorityKeyIdentifier=keyid:always + +[ ocsp ] +# Extension for OCSP signing certificates. +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid,issuer +keyUsage = critical, digitalSignature +extendedKeyUsage = critical, OCSPSigning diff --git a/azure_iot_edge/tests/tls/setup.sh b/azure_iot_edge/tests/tls/setup.sh new file mode 100755 index 0000000000000..2d3b9a7a29861 --- /dev/null +++ b/azure_iot_edge/tests/tls/setup.sh @@ -0,0 +1,30 @@ +#! /bin/bash -e + +# Automated script for steps described in: +# Adapted from: https://docs.microsoft.com/en-us/azure/iot-edge/how-to-create-test-certificates + +DIR="azure_iot_edge/tests/tls" + +cd $DIR + +TEST_DEVICE_NAME='testEdgeDevice' +CA_CERT_NAME='mighty-candy' # Something unrelated to the device name. + +# Create root CA certificate. +./certGen.sh create_root_and_intermediate +echo 'SUCCESS: created root CA certificate:' +echo + +# Verify root CA certificate so that test device can communicate with IoT Hub. +echo "Please upload $DIR/certs/azure-iot-test-only.root.ca.cert.pem to IoT Hub to generate downstream device certificate, then generate a verification code." +echo 'See: https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-security-x509-get-started#register-x509-ca-certificates-to-your-iot-hub' +echo 'Enter verification code:' +read VERIFICATION_CODE +./certGen.sh create_verification_certificate $VERIFICATION_CODE +./certGen.sh create_device_certificate $TEST_DEVICE_NAME +echo +echo 'SUCCESS: created device certificate files' +echo +echo "Please upload $DIR/certs/verification-code.cert.pem to IoT Hub to complete certificate validation" +echo "See steps 7/ and 8/ here: https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-security-x509-get-started#register-x509-ca-certificates-to-your-iot-hub" +echo "Once done, certificate should show as 'Verified' in IoT Hub web UI." diff --git a/azure_iot_edge/tox.ini b/azure_iot_edge/tox.ini new file mode 100644 index 0000000000000..9c3242feb4203 --- /dev/null +++ b/azure_iot_edge/tox.ini @@ -0,0 +1,35 @@ +[tox] +minversion = 2.0 +skip_missing_interpreters = true +basepython = py38 +envlist = + py{27,38} + py{27,38}-tls + +[testenv] +ensure_default_envdir = true +envdir = + py27: {toxworkdir}/py27 + py38: {toxworkdir}/py38 +dd_check_style = true +dd_check_types = true +dd_mypy_args = --check-untyped-defs --py2 datadog_checks/ tests/ +description = + py{27,38}: e2e ready if IOT_EDGE_CONNSTR +usedevelop = true +platform = linux|darwin|win32 +deps = + -e../datadog_checks_base[deps] + -rrequirements-dev.txt +passenv = + DOCKER* + COMPOSE* + IOT_EDGE_* +commands = + pip install -r requirements.in + pytest -v {posargs} +setenv = + IOT_EDGE_E2E_LIBIOTHSM_STD_URL = https://github.com/Azure/azure-iotedge/releases/download/1.0.10-rc2/libiothsm-std_1.0.10.rc2-1_ubuntu16.04_amd64.deb + IOT_EDGE_E2E_IOTEDGE_URL = https://github.com/Azure/azure-iotedge/releases/download/1.0.10-rc2/iotedge_1.0.10.rc2-1_ubuntu16.04_amd64.deb + IOT_EDGE_E2E_IMAGE = mcr.microsoft.com/azureiotedge-agent:1.0.10-rc2 + tls: IOT_EDGE_E2E_TLS_ENABLED = true