diff --git a/samcli/cli/global_config.py b/samcli/cli/global_config.py index b4f587ce84..f628091ffb 100644 --- a/samcli/cli/global_config.py +++ b/samcli/cli/global_config.py @@ -62,6 +62,9 @@ class GlobalConfig(metaclass=Singleton): # Env var for injecting dir in integration tests _DIR_INJECTION_ENV_VAR: str = "__SAM_CLI_APP_DIR" + # Env var used by docker client to specify which socket to use + DOCKER_HOST_ENV_VAR: str = "DOCKER_HOST" + # Static singleton instance _access_lock: threading.RLock @@ -71,6 +74,7 @@ class GlobalConfig(metaclass=Singleton): _config_data: Optional[Dict[str, Any]] # config_keys that should be flushed to file _persistent_fields: List[str] + docker_host: str def __init__(self): """__init__ should only be called once due to Singleton metaclass""" @@ -79,6 +83,7 @@ def __init__(self): self._config_filename = None self._config_data = None self._persistent_fields = list() + self.docker_host = os.environ.get(GlobalConfig.DOCKER_HOST_ENV_VAR, "") @property def config_dir(self) -> Path: diff --git a/samcli/lib/telemetry/metric.py b/samcli/lib/telemetry/metric.py index 7f3127cf86..328b9b2ae6 100644 --- a/samcli/lib/telemetry/metric.py +++ b/samcli/lib/telemetry/metric.py @@ -7,7 +7,7 @@ import uuid from dataclasses import dataclass from functools import reduce, wraps -from pathlib import Path +from pathlib import Path, PurePath from timeit import default_timer from typing import Any, Dict, Optional @@ -436,6 +436,7 @@ def _add_common_metric_attributes(self): self._data["installationId"] = self._gc.installation_id self._data["sessionId"] = self._session_id self._data["executionEnvironment"] = self._get_execution_environment() + self._data["dockerHost"] = self._get_docker_host() self._data["ci"] = bool(self._cicd_detector.platform()) self._data["pyversion"] = platform.python_version() self._data["samcliVersion"] = samcli_version @@ -476,6 +477,22 @@ def _get_execution_environment(self) -> str: return cicd_platform.name return "CLI" + def _get_docker_host(self) -> str: + """ + Returns the last part of a DOCKER_HOST string. + If the DOCKER_HOST is not set, returns an empty string. + + Examples: + - unix:///var/run/docker.sock -> docker.sock + - tcp://localhost:1234 -> localhost:1234 + - /var/run/docker.sock -> docker.sock + """ + + try: + return PurePath(self._gc.docker_host).name + except TypeError: + return "" + class MetricDataNotList(Exception): pass diff --git a/tests/integration/telemetry/integ_base.py b/tests/integration/telemetry/integ_base.py index 157fd78962..89b3c68f5a 100644 --- a/tests/integration/telemetry/integ_base.py +++ b/tests/integration/telemetry/integ_base.py @@ -39,6 +39,7 @@ def setUp(self): self.config_dir = tempfile.mkdtemp() self._gc = GlobalConfig() self._gc.config_dir = Path(self.config_dir) + self._gc.docker_host = "" def tearDown(self): self.config_dir and shutil.rmtree(self.config_dir) diff --git a/tests/integration/telemetry/test_experimental_metric.py b/tests/integration/telemetry/test_experimental_metric.py index 1c8ca5b223..efb312f00c 100644 --- a/tests/integration/telemetry/test_experimental_metric.py +++ b/tests/integration/telemetry/test_experimental_metric.py @@ -58,6 +58,7 @@ def test_must_send_experimental_metrics_if_experimental_command(self): "debugFlagProvided": ANY, "region": ANY, "commandName": ANY, + "dockerHost": ANY, "metricSpecificAttributes": { "experimentalAll": False, "experimentalEsbuild": False, @@ -115,6 +116,7 @@ def test_must_send_experimental_metrics_if_experimental_option(self): "debugFlagProvided": ANY, "region": ANY, "commandName": ANY, + "dockerHost": ANY, "metricSpecificAttributes": { "experimentalAll": True, "experimentalEsbuild": True, @@ -182,6 +184,7 @@ def test_must_send_cdk_project_type_metrics(self): "debugFlagProvided": ANY, "region": ANY, "commandName": ANY, + "dockerHost": ANY, "metricSpecificAttributes": { "projectType": "CDK", "gitOrigin": ANY, @@ -232,6 +235,7 @@ def test_must_send_not_experimental_metrics_if_not_experimental(self): "executionEnvironment": ANY, "ci": ANY, "pyversion": ANY, + "dockerHost": ANY, "samcliVersion": SAM_CLI_VERSION, "awsProfileProvided": ANY, "debugFlagProvided": ANY, diff --git a/tests/integration/telemetry/test_installed_metric.py b/tests/integration/telemetry/test_installed_metric.py index 0b0d2d550e..09dff793cd 100644 --- a/tests/integration/telemetry/test_installed_metric.py +++ b/tests/integration/telemetry/test_installed_metric.py @@ -42,6 +42,7 @@ def test_send_installed_metric_on_first_run(self): { "installed": { "installationId": self.get_global_config().installation_id, + "dockerHost": ANY, "samcliVersion": SAM_CLI_VERSION, "osPlatform": platform.system(), "executionEnvironment": ANY, diff --git a/tests/unit/lib/telemetry/test_event.py b/tests/unit/lib/telemetry/test_event.py index 33202303cf..c804c26868 100644 --- a/tests/unit/lib/telemetry/test_event.py +++ b/tests/unit/lib/telemetry/test_event.py @@ -167,6 +167,7 @@ def test_events_get_sent(self, telemetry_mock): "pyversion": ANY, "samcliVersion": ANY, "commandName": ANY, + "dockerHost": ANY, "metricSpecificAttributes": { "events": [ { diff --git a/tests/unit/lib/telemetry/test_metric.py b/tests/unit/lib/telemetry/test_metric.py index 22f695eebe..0ba9aacb31 100644 --- a/tests/unit/lib/telemetry/test_metric.py +++ b/tests/unit/lib/telemetry/test_metric.py @@ -43,6 +43,7 @@ def test_must_send_installed_metric_with_attributes(self, TelemetryClassMock): telemetry_mock = TelemetryClassMock.return_value = Mock() self.gc_mock.return_value.telemetry_enabled = False + self.gc_mock.return_value.docker_host = "" send_installed_metric() args, _ = telemetry_mock.emit.call_args_list[0] metric = args[0] @@ -73,6 +74,7 @@ def setUp(self): # Enable telemetry so we can actually run the tests self.gc_instance_mock.telemetry_enabled = True + self.gc_instance_mock.docker_host = "" def tearDown(self): self.telemetry_class_patcher.stop() @@ -310,6 +312,7 @@ def setUp(self): # Enable telemetry so we can actually run the tests self.gc_instance_mock.telemetry_enabled = True + self.gc_instance_mock.docker_host = "" def tearDown(self): self.telemetry_class_patcher.stop() @@ -524,6 +527,7 @@ def test_must_add_common_attributes( ): request_id = uuid_mock.uuid4.return_value = "fake requestId" installation_id = gc_mock.return_value.installation_id = "fake installation id" + docker_host = gc_mock.return_value.docker_host = "fake docker host" session_id = context_mock.get_current_context.return_value.session_id = "fake installation id" python_version = platform_mock.python_version.return_value = "8.8.0" cicd_platform_mock.return_value = cicd_platform @@ -540,10 +544,19 @@ def test_must_add_common_attributes( assert metric.get_data()["userAgent"] == user_agent assert metric.get_data()["pyversion"] == python_version assert metric.get_data()["samcliVersion"] == samcli.__version__ + assert metric.get_data()["dockerHost"] == docker_host def _ignore_common_attributes(data): - common_attrs = ["requestId", "installationId", "sessionId", "executionEnvironment", "pyversion", "samcliVersion"] + common_attrs = [ + "requestId", + "installationId", + "sessionId", + "executionEnvironment", + "pyversion", + "samcliVersion", + "dockerHost", + ] for a in common_attrs: if a not in data: data[a] = ANY