Skip to content

Commit

Permalink
Add-usage-analytics (#39)
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbraun89 authored Apr 10, 2023
1 parent 8e945d2 commit b056eb9
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 21 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/release-binaries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ jobs:

run: |
sudo apt-get install -y build-essential musl musl-dev musl-tools
sudo ld-musl-config
# NOTE: keep version at 0.23.0: https://github.com/indygreg/PyOxidizer/issues/673
python3 -m pip install pyoxidizer==0.23.0
Expand Down
4 changes: 2 additions & 2 deletions nanolayer/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@

def version_callback(value: bool) -> None:
if value:
typer.echo(f"nanolayer version: {resolve_own_package_version()}")
typer.echo(resolve_own_package_version())
raise typer.Exit()


def release_version_callback(value: bool) -> None:
if value:
typer.echo(f"nanolayer release version: {resolve_own_release_version()}")
typer.echo(resolve_own_release_version())
raise typer.Exit()


Expand Down
61 changes: 61 additions & 0 deletions nanolayer/cli/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import logging
import os
import platform

import sentry_sdk

from nanolayer.utils.linux_information_desk import EnvFile, ProcFile
from nanolayer.utils.settings import NanolayerSettings
from nanolayer.utils.version import resolve_own_package_version

logger = logging.getLogger(__name__)

try:
dsn = NanolayerSettings().analytics_id if not NanolayerSettings().no_analytics else ""
sentry_sdk.init(
release=resolve_own_package_version(),
traces_sample_rate=1.0,
dsn=dsn,
# explicitly turn off personally identifiable information
send_default_pii=False,
# explicitly turn off client reports
send_client_reports=False,
# explicitly turn off client reports
request_bodies="never",
)
# explicitly strip any identifiable user information
sentry_sdk.set_user(None)

# ------ add only non-personally-identifiable hardware metrics -------
os_release_file = EnvFile.parse("/etc/os-release")
meminfo_file = ProcFile.parse("/proc/meminfo")

sentry_sdk.set_context(
"nanolayer",
{
# uname -a like
"uname": str(platform.uname()),
# num of cores
"cpu_count": os.cpu_count(),
# 4GB 8GB 16GB etc
"total_memory": meminfo_file['MemTotal'],
# 4GB 8GB etc
"free_memory": meminfo_file['MemFree'],
},
)

# architecture such as x86_64, ARM etc
sentry_sdk.set_tag("architecture", platform.machine())
# distro id, eg ubuntu / debian / alpine / fedora
sentry_sdk.set_tag("os_release_id", os_release_file.get("ID", None))
# distro id category, for example "debian" for both debian and ubuntu
sentry_sdk.set_tag("os_release_id_like", os_release_file.get("ID_LIKE", None))
# distro version, for example for ubuntu it will be 18.04 20.04 22.04 etc
sentry_sdk.set_tag(
"os_release_version_id", os_release_file.get("VERSION_ID", None)
)
# boolean - true if nanolayer is being used as a binary, false if used as a python package
sentry_sdk.set_tag("nanolayer.binary_mode", "__file__" not in globals())

except Exception as e: # no qa
logger.warning("usage metrics are disabled")
2 changes: 1 addition & 1 deletion nanolayer/installers/gh_release/gh_release_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def get_file_members(self) -> List[str]:
class ExtendedZipFile(ZipFile, AbstractExtendedArchive):
def get_file_members(self) -> List[str]:
members = self.namelist()
return [member for member in members if not member.endswith('/')]
return [member for member in members if not member.endswith("/")]

def get_names_by_prefix(self, prefix: str) -> None:
subdir_and_files = [
Expand Down
32 changes: 23 additions & 9 deletions nanolayer/utils/linux_information_desk.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,28 @@
from typing import Dict


class EnvFile:
@staticmethod
def parse(path: str) -> Dict[str, str]:
with open(path, "r") as f:
return dict(
tuple(line.replace("\n", "").split("="))
for line in f.readlines()
if not line.startswith("#")
)

class ProcFile:

@staticmethod
def parse(path: str) -> Dict[str, str]:
items = {}
with open(path, "r") as f:
for line in f.readlines():
splitted_values = line.split(":")
if len(splitted_values) == 2:
items[splitted_values[0].strip()] = splitted_values[1].strip()
return items

class LinuxInformationDesk:
OS_RELEASE_PATH = "/etc/os-release"

Expand Down Expand Up @@ -58,15 +80,7 @@ class LinuxReleaseID(Enum):
def _get_release_id_str(cls, id_like: bool = False) -> str:
assert cls.has_root_privileges()

def _parse_env_file(path: str) -> Dict[str, str]:
with open(path, "r") as f:
return dict(
tuple(line.replace("\n", "").split("="))
for line in f.readlines()
if not line.startswith("#")
)

parsed_os_release = _parse_env_file(cls.OS_RELEASE_PATH)
parsed_os_release = EnvFile.parse(cls.OS_RELEASE_PATH)

if id_like:
os_release_id = parsed_os_release.get("ID_LIKE", None)
Expand Down
8 changes: 8 additions & 0 deletions nanolayer/utils/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,18 @@ class Config:
cli_location: str = ""
propagate_cli_location: str = "1"
force_cli_installation: str = ""

analytics_id: str = "https://2a5d4cc20cb94a8cbb691df3bcc69f0f@o4504983808901120.ingest.sentry.io/4504983813685248"
no_analytics: bool = False

verbose: str = ""


ENV_CLI_LOCATION = f"{NanolayerSettings.Config.env_prefix}CLI_LOCATION"

ENV_NO_ANALYTICS = f"{NanolayerSettings.Config.env_prefix}NO_ANALYTICS"
ENV_ANALYTICS_ID = f"{NanolayerSettings.Config.env_prefix}ANALYTICS_ID"

ENV_PROPAGATE_CLI_LOCATION = (
f"{NanolayerSettings.Config.env_prefix}PROPAGATE_CLI_LOCATION"
)
Expand Down
9 changes: 5 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pydantic==1.10.5
typer
invoke
semver==2.13.0
pydantic==1.10.7
typer==0.7.0
invoke==2.0.0
semver==2.13.0
sentry-sdk==1.19.1
8 changes: 4 additions & 4 deletions tests/installers/gh_release/test_gh_release_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@pytest.mark.parametrize(
"test_command,excpected_result,image,repo,target,docker_platform",
[
(
(
"upx --version",
0,
"mcr.microsoft.com/devcontainers/base:debian",
Expand All @@ -24,7 +24,7 @@
"doctl",
"linux/amd64",
),
(# classic
( # classic
"argocd --help",
0,
"mcr.microsoft.com/devcontainers/base:debian",
Expand All @@ -48,15 +48,15 @@
"argocd",
"linux/arm64",
),
( # two binaries at same repo
( # two binaries at same repo
"which kubectx",
0,
"mcr.microsoft.com/devcontainers/base:debian",
"ahmetb/kubectx",
"kubectx",
"linux/amd64",
),
( # two binaries at same repo
( # two binaries at same repo
"which kubens",
0,
"mcr.microsoft.com/devcontainers/base:debian",
Expand Down

0 comments on commit b056eb9

Please sign in to comment.