From 1f2fdae997a6812c3672268b16a2fe024b5b173a Mon Sep 17 00:00:00 2001 From: Augustin Date: Thu, 21 Apr 2022 12:17:46 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=81=20octavia-cli:=20improve=20telemet?= =?UTF-8?q?ry=20(#12072)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- octavia-cli/README.md | 17 ++++++----- octavia-cli/install.sh | 24 +++++++++++++-- octavia-cli/octavia_cli/entrypoint.py | 12 ++++++-- octavia-cli/octavia_cli/telemetry.py | 11 +++---- octavia-cli/unit_tests/test_entrypoint.py | 18 ++++++++++- octavia-cli/unit_tests/test_telemetry.py | 37 +++++++++++++++-------- 6 files changed, 87 insertions(+), 32 deletions(-) diff --git a/octavia-cli/README.md b/octavia-cli/README.md index b399f0b09a0c..39c979a00cea 100644 --- a/octavia-cli/README.md +++ b/octavia-cli/README.md @@ -352,17 +352,18 @@ $ octavia apply ## Telemetry This CLI has some telemetry tooling to send Airbyte some data about the usage of this tool. -We use this data to measure the tool's adoption and detect common errors users encounter to improve it. +We will use this data to improve the CLI and measure its adoption. The telemetry sends data about: * Which command was run (not the arguments or options used). -* Success or failure of the command run and the error type. -* The workspace id. It is unique to each Airbyte instance. +* Success or failure of the command run and the error type (not the error payload). +* The current Airbyte workspace id if the user has not set the *anonymous data collection* on their Airbyte instance. -You can disable telemetry by setting the `OCTAVIA_ENABLE_TELEMETRY` environment to `false` or using the `--disable-telemetry` flag. +You can disable telemetry by setting the `OCTAVIA_ENABLE_TELEMETRY` environment variable to `False` or using the `--disable-telemetry` flag. ## Changelog -| Version | Date | Description | PR | -|---------|------------|------------------|----------------------------------------------------------| -| 0.35.68 | 2022-04-12 | Add telemetry | [#11896](https://github.com/airbytehq/airbyte/issues/11896)| -| 0.35.61 | 2022-04-07 | Alpha release | [EPIC](https://github.com/airbytehq/airbyte/issues/10704)| +| Version | Date | Description | PR | +|---------|------------|-------------------|----------------------------------------------------------| +| 0.36.2 | 2022-04-15 | Improve telemetry | [#12072](https://github.com/airbytehq/airbyte/issues/11896)| +| 0.35.68 | 2022-04-12 | Add telemetry | [#11896](https://github.com/airbytehq/airbyte/issues/11896)| +| 0.35.61 | 2022-04-07 | Alpha release | [EPIC](https://github.com/airbytehq/airbyte/issues/10704)| diff --git a/octavia-cli/install.sh b/octavia-cli/install.sh index c65a3513878a..3601592a2c2c 100755 --- a/octavia-cli/install.sh +++ b/octavia-cli/install.sh @@ -40,11 +40,13 @@ delete_previous_alias() { pull_image() { + echo "🐙 - Pulling image for octavia ${VERSION}" docker pull airbyte/octavia-cli:${VERSION} > /dev/null 2>&1 + echo "🐙 - 🎉 octavia ${VERSION} image was pulled" } add_octavia_comment_to_profile() { - printf "\n# OCTAVIA CLI\n" >> ${DETECTED_PROFILE} + printf "\n# OCTAVIA CLI ${VERSION}\n" >> ${DETECTED_PROFILE} } create_octavia_env_file() { @@ -53,10 +55,15 @@ create_octavia_env_file() { echo "🐙 - 💾 The octavia env file was created at ${OCTAVIA_ENV_FILE}" } +enable_telemetry() { + echo "export OCTAVIA_ENABLE_TELEMETRY=$1" >> ${DETECTED_PROFILE} + echo "OCTAVIA_ENABLE_TELEMETRY=$1" >> ${OCTAVIA_ENV_FILE} +} add_alias() { echo 'alias octavia="docker run -i --rm -v \$(pwd):/home/octavia-project --network host --env-file \${OCTAVIA_ENV_FILE} --user \$(id -u):\$(id -g) airbyte/octavia-cli:'${VERSION}'"' >> ${DETECTED_PROFILE} - echo "🐙 - 🎉 octavia alias was added to ${DETECTED_PROFILE}, please open a new terminal window or run source ${DETECTED_PROFILE}" + echo "🐙 - 🎉 octavia alias was added to ${DETECTED_PROFILE}!" + echo "🐙 - Please open a new terminal window or run source ${DETECTED_PROFILE}" } install() { @@ -64,10 +71,20 @@ install() { add_alias } +telemetry_consent() { + read -p "❓ - Allow Airbyte to collect telemetry to improve the CLI? (Y/n)" -n 1 -r None: @@ -94,6 +96,12 @@ def get_workspace_id(api_client, user_defined_workspace_id): return api_response.workspaces[0]["workspaceId"] +def get_anonymous_data_collection(api_client, workspace_id): + api_instance = workspace_api.WorkspaceApi(api_client) + api_response = api_instance.get_workspace(WorkspaceIdRequestBody(workspace_id), _check_return_type=False) + return api_response.anonymous_data_collection + + def add_commands_to_octavia(): for command in AVAILABLE_COMMANDS: octavia.add_command(command) diff --git a/octavia-cli/octavia_cli/telemetry.py b/octavia-cli/octavia_cli/telemetry.py index 73330fd65d0c..f952fcb83669 100644 --- a/octavia-cli/octavia_cli/telemetry.py +++ b/octavia-cli/octavia_cli/telemetry.py @@ -10,17 +10,16 @@ import click -def build_user_agent(octavia_version: str, workspace_id: str) -> str: - """Build user-agent for the API client according to octavia version and workspace id. +def build_user_agent(octavia_version: str) -> str: + """Build user-agent for the API client according to octavia version. Args: octavia_version (str): Current octavia version. - workspace_id (str): Current workspace id. Returns: str: the user-agent string. """ - return f"octavia-cli/{octavia_version}/{workspace_id}" + return f"octavia-cli/{octavia_version}" class TelemetryClient: @@ -75,7 +74,7 @@ def send_command_telemetry(self, ctx: click.Context, error: Optional[Exception] error (Optional[Exception], optional): The error that was raised. Defaults to None. extra_info_name (Optional[str], optional): Extra info name if the context was not built yet. Defaults to None. """ - user_id = ctx.obj.get("WORKSPACE_ID") + user_id = ctx.obj.get("WORKSPACE_ID") if ctx.obj.get("ANONYMOUS_DATA_COLLECTION", True) is False else None anonymous_id = None if user_id else str(uuid.uuid1()) segment_context = {"app": {"name": "octavia-cli", "version": ctx.obj.get("OCTAVIA_VERSION")}} @@ -83,7 +82,7 @@ def send_command_telemetry(self, ctx: click.Context, error: Optional[Exception] "success": error is None, "error_type": error.__class__.__name__ if error is not None else None, "project_is_initialized": ctx.obj.get("PROJECT_IS_INITIALIZED"), - "airbyte_role": os.getenv("AIRBYTE_ROLE"), + "airbyter": os.getenv("AIRBYTE_ROLE") == "airbyter", } command_name = self._create_command_name(ctx, extra_info_name) self.segment_client.track( diff --git a/octavia-cli/unit_tests/test_entrypoint.py b/octavia-cli/unit_tests/test_entrypoint.py index 25b5e1d042e0..cc4e866c5753 100644 --- a/octavia-cli/unit_tests/test_entrypoint.py +++ b/octavia-cli/unit_tests/test_entrypoint.py @@ -5,6 +5,7 @@ import click import pkg_resources import pytest +from airbyte_api_client.model.workspace_id_request_body import WorkspaceIdRequestBody from click.testing import CliRunner from octavia_cli import entrypoint @@ -17,21 +18,25 @@ def dumb(ctx): def test_set_context_object(mocker): mocker.patch.object(entrypoint, "TelemetryClient") + mocker.patch.object(entrypoint, "build_user_agent") mocker.patch.object(entrypoint, "get_api_client") mocker.patch.object(entrypoint, "get_workspace_id") mocker.patch.object(entrypoint, "build_user_agent") mocker.patch.object(entrypoint, "check_is_initialized") + mocker.patch.object(entrypoint, "get_anonymous_data_collection") mock_ctx = mocker.Mock(obj={}) built_context = entrypoint.set_context_object(mock_ctx, "my_airbyte_url", "my_workspace_id", "enable_telemetry") entrypoint.TelemetryClient.assert_called_with("enable_telemetry") mock_ctx.ensure_object.assert_called_with(dict) - built_context.obj == { + assert built_context.obj == { "OCTAVIA_VERSION": pkg_resources.require("octavia-cli")[0].version, "TELEMETRY_CLIENT": entrypoint.TelemetryClient.return_value, "WORKSPACE_ID": entrypoint.get_workspace_id.return_value, "API_CLIENT": entrypoint.get_api_client.return_value, "PROJECT_IS_INITIALIZED": entrypoint.check_is_initialized.return_value, + "ANONYMOUS_DATA_COLLECTION": entrypoint.get_anonymous_data_collection.return_value, } + entrypoint.build_user_agent.assert_called_with(built_context.obj["OCTAVIA_VERSION"]) def test_set_context_object_error(mocker): @@ -106,6 +111,17 @@ def test_get_workspace_id_api_defined(mocker): mock_api_instance.list_workspaces.assert_called_with(_check_return_type=False) +def test_get_anonymous_data_collection(mocker, mock_api_client): + mocker.patch.object(entrypoint, "workspace_api") + mock_api_instance = entrypoint.workspace_api.WorkspaceApi.return_value + assert ( + entrypoint.get_anonymous_data_collection(mock_api_client, "my_workspace_id") + == mock_api_instance.get_workspace.return_value.anonymous_data_collection + ) + entrypoint.workspace_api.WorkspaceApi.assert_called_with(mock_api_client) + mock_api_instance.get_workspace.assert_called_with(WorkspaceIdRequestBody("my_workspace_id"), _check_return_type=False) + + def test_commands_in_octavia_group(): octavia_commands = entrypoint.octavia.commands.values() for command in entrypoint.AVAILABLE_COMMANDS: diff --git a/octavia-cli/unit_tests/test_telemetry.py b/octavia-cli/unit_tests/test_telemetry.py index 36cbf14c8861..3fc5446b696b 100644 --- a/octavia-cli/unit_tests/test_telemetry.py +++ b/octavia-cli/unit_tests/test_telemetry.py @@ -8,8 +8,8 @@ def test_build_user_agent(): - ua = telemetry.build_user_agent("my_octavia_version", "my_workspace_id") - assert ua == "octavia-cli/my_octavia_version/my_workspace_id" + ua = telemetry.build_user_agent("my_octavia_version") + assert ua == "octavia-cli/my_octavia_version" class TestTelemetryClient: @@ -54,14 +54,25 @@ def test__create_command_name_single_context(self, mocker, telemetry_client, ext assert command_name == "child_command" @pytest.mark.parametrize( - "workspace_id,airbyte_role,project_is_initialized,octavia_version,error, expected_success, expected_error_type", + "workspace_id, anonymous_data_collection, airbyte_role, project_is_initialized, octavia_version, error, expected_success, expected_error_type", [ - (None, None, None, None, None, True, None), - ("my_workspace_id", "my_airbyte_role", True, "0.1.0", None, True, None), - ("my_workspace_id", "my_airbyte_role", False, "0.1.0", None, True, None), - ("my_workspace_id", "my_airbyte_role", False, "0.1.0", AttributeError(), False, "AttributeError"), - ("my_workspace_id", "my_airbyte_role", True, "0.1.0", AttributeError(), False, "AttributeError"), - (None, None, True, "0.1.0", AttributeError(), False, "AttributeError"), + (None, None, None, None, None, None, True, None), + (None, None, None, None, None, Exception(), False, "Exception"), + (None, None, None, None, None, AttributeError(), False, "AttributeError"), + (None, True, None, None, None, None, True, None), + (None, True, None, None, None, Exception(), False, "Exception"), + (None, True, None, None, None, AttributeError(), False, "AttributeError"), + ("my_workspace_id", False, None, None, None, None, True, None), + ("my_workspace_id", False, None, None, None, Exception(), False, "Exception"), + ("my_workspace_id", True, None, None, None, None, True, None), + ("my_workspace_id", True, None, None, None, Exception(), False, "Exception"), + ("my_workspace_id", True, "airbyter", None, None, None, True, None), + ("my_workspace_id", True, "non_airbyter", None, None, Exception(), False, "Exception"), + ("my_workspace_id", True, "airbyter", True, None, None, True, None), + ("my_workspace_id", True, "non_airbyter", False, None, Exception(), False, "Exception"), + ("my_workspace_id", True, "airbyter", True, None, None, True, None), + ("my_workspace_id", True, "non_airbyter", False, "0.1.0", Exception(), False, "Exception"), + ("my_workspace_id", True, "non_airbyter", False, "0.1.0", None, True, None), ], ) def test_send_command_telemetry( @@ -69,6 +80,7 @@ def test_send_command_telemetry( mocker, telemetry_client, workspace_id, + anonymous_data_collection, airbyte_role, project_is_initialized, octavia_version, @@ -79,13 +91,14 @@ def test_send_command_telemetry( extra_info_name = "foo" mocker.patch.object(telemetry.os, "getenv", mocker.Mock(return_value=airbyte_role)) mocker.patch.object(telemetry.uuid, "uuid1", mocker.Mock(return_value="MY_UUID")) - expected_user_id = workspace_id if workspace_id is not None else None - expected_anonymous_id = "MY_UUID" if workspace_id is None else None + expected_user_id = workspace_id if workspace_id is not None and anonymous_data_collection is False else None + expected_anonymous_id = "MY_UUID" if expected_user_id is None else None mock_ctx = mocker.Mock( obj={ "OCTAVIA_VERSION": octavia_version, "PROJECT_IS_INITIALIZED": project_is_initialized, "WORKSPACE_ID": workspace_id, + "ANONYMOUS_DATA_COLLECTION": anonymous_data_collection, } ) expected_segment_context = {"app": {"name": "octavia-cli", "version": octavia_version}} @@ -93,7 +106,7 @@ def test_send_command_telemetry( "success": expected_success, "error_type": expected_error_type, "project_is_initialized": project_is_initialized, - "airbyte_role": airbyte_role, + "airbyter": airbyte_role == "airbyter", } telemetry_client.segment_client = mocker.Mock() telemetry_client._create_command_name = mocker.Mock(return_value="my_command")