Skip to content

Commit

Permalink
Migrate E2E features
Browse files Browse the repository at this point in the history
  • Loading branch information
ofek committed Sep 30, 2023
1 parent c7746ad commit abc791a
Show file tree
Hide file tree
Showing 28 changed files with 1,428 additions and 46 deletions.
4 changes: 4 additions & 0 deletions datadog_checks_dev/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

***Added***:

* Migrate E2E features ([#15931](https://github.com/DataDog/integrations-core/pull/15931))

## 25.1.2 / 2023-09-26

***Fixed***:
Expand Down
2 changes: 1 addition & 1 deletion datadog_checks_dev/datadog_checks/dev/_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
E2E_ENV_VAR_PREFIX = '{}_ENV_'.format(E2E_PREFIX)
E2E_SET_UP = '{}_UP'.format(E2E_PREFIX)
E2E_TEAR_DOWN = '{}_DOWN'.format(E2E_PREFIX)
E2E_PARENT_PYTHON = '{}_PYTHON_PATH'.format(E2E_PREFIX)
E2E_PARENT_PYTHON = '{}_PARENT_PYTHON'.format(E2E_PREFIX)
E2E_RESULT_FILE = '{}_RESULT_FILE'.format(E2E_PREFIX)

E2E_FIXTURE_NAME = 'dd_environment'
Expand Down
20 changes: 14 additions & 6 deletions datadog_checks_dev/datadog_checks/dev/plugin/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@
import warnings
from base64 import urlsafe_b64encode
from collections import namedtuple # Not using dataclasses for Py2 compatibility
from io import open
from typing import Dict, List, Optional, Tuple # noqa: F401

import pytest
from six import PY2
from six import PY2, ensure_text

from .._env import (
E2E_FIXTURE_NAME,
Expand Down Expand Up @@ -139,7 +140,7 @@ def dd_environment_runner(request):
# Exit testing and pass data back up to command
if E2E_RESULT_FILE in os.environ:
with open(os.environ[E2E_RESULT_FILE], 'w', encoding='utf-8') as f:
f.write(json.dumps(data))
f.write(ensure_text(json.dumps(data)))

# Rather than exiting we skip every test to avoid the following output:
# !!!!!!!!!! _pytest.outcomes.Exit: !!!!!!!!!!
Expand All @@ -160,7 +161,8 @@ def pytest_report_teststatus(report, config):
"""
# Skipping every test displays an `s` for each even when using
# the minimum verbosity so we force zero output
return 'skipped', '', ''
if report.skipped:
return 'skipped', '', ''


@pytest.fixture
Expand All @@ -187,8 +189,7 @@ def run_check(config=None, **kwargs):
python_path = os.environ[E2E_PARENT_PYTHON]
env = os.environ['HATCH_ENV_ACTIVE']

# TODO: switch to `ddev` when the old CLI is gone
check_command = [python_path, '-m', 'datadog_checks.dev', 'env', 'check', check, env, '--json']
check_command = [python_path, '-m', 'ddev', 'env', 'agent', check, env, 'check', '--json']

if config:
config = format_config(config)
Expand All @@ -197,7 +198,14 @@ def run_check(config=None, **kwargs):
with open(config_file, 'wb') as f:
output = json.dumps(config).encode('utf-8')
f.write(output)
check_command.extend(['--config', config_file])
check_command.extend(['--config-file', config_file])

# TODO: remove these legacy flags when all usage of this fixture is migrated
if 'rate' in kwargs:
kwargs['check_rate'] = kwargs.pop('rate')

if 'times' in kwargs:
kwargs['check_times'] = kwargs.pop('times')

for key, value in kwargs.items():
if value is not False:
Expand Down
4 changes: 4 additions & 0 deletions ddev/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

***Added***:

* Migrate E2E features ([#15931](https://github.com/DataDog/integrations-core/pull/15931))

## 5.1.1 / 2023-09-29

***Fixed***:
Expand Down
2 changes: 1 addition & 1 deletion ddev/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ classifiers = [
dependencies = [
"click~=8.1.6",
"coverage",
"datadog-checks-dev[cli]~=25.0",
"datadog-checks-dev[cli]>=25.1.2,<26",
"hatch>=1.6.3",
"httpx",
"jsonpointer",
Expand Down
11 changes: 9 additions & 2 deletions ddev/src/ddev/cli/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
from __future__ import annotations

import os
from functools import cached_property
from typing import cast

from ddev.cli.terminal import Terminal
from ddev.config.constants import AppEnvVars, VerbosityLevels
from ddev.config.constants import AppEnvVars, ConfigEnvVars, VerbosityLevels
from ddev.config.file import ConfigFile, RootConfig
from ddev.repo.core import Repository
from ddev.utils.fs import Path
Expand All @@ -18,7 +19,7 @@
class Application(Terminal):
def __init__(self, exit_func, *args, **kwargs):
super().__init__(*args, **kwargs)
self.platform = Platform(self.display_raw)
self.platform = Platform(self.output)
self.__exit_func = exit_func

self.config_file = ConfigFile()
Expand All @@ -40,6 +41,12 @@ def config(self) -> RootConfig:
def repo(self) -> Repository:
return self.__repo

@cached_property
def data_dir(self) -> Path:
from platformdirs import user_data_dir

return Path(os.getenv(ConfigEnvVars.DATA) or user_data_dir('ddev', appauthor=False)).expand()

@property
def github(self) -> GitHubManager:
return self.__github
Expand Down
4 changes: 2 additions & 2 deletions ddev/src/ddev/cli/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def show(app, all_keys):
from rich.syntax import Syntax

text = app.config_file.read() if all_keys else app.config_file.read_scrubbed()
app.display_raw(Syntax(text.rstrip(), 'toml', background_color='default'))
app.output(Syntax(text.rstrip(), 'toml', background_color='default'))


@config.command(short_help='Update the config file with any new fields')
Expand Down Expand Up @@ -146,4 +146,4 @@ def set_value(app, key, value):
from rich.syntax import Syntax

app.display_success('New setting:')
app.display_raw(Syntax(rendered_changed, 'toml', background_color='default'))
app.output(Syntax(rendered_changed, 'toml', background_color='default'))
31 changes: 16 additions & 15 deletions ddev/src/ddev/cli/env/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
import click
from datadog_checks.dev.tooling.commands.env.check import check_run
from datadog_checks.dev.tooling.commands.env.edit import edit
from datadog_checks.dev.tooling.commands.env.ls import ls
from datadog_checks.dev.tooling.commands.env.prune import prune
from datadog_checks.dev.tooling.commands.env.reload import reload_env
from datadog_checks.dev.tooling.commands.env.shell import shell
from datadog_checks.dev.tooling.commands.env.start import start
from datadog_checks.dev.tooling.commands.env.stop import stop
from datadog_checks.dev.tooling.commands.env.test import test

from ddev.cli.env.agent import agent
from ddev.cli.env.check import check
from ddev.cli.env.config import config
from ddev.cli.env.reload import reload_command
from ddev.cli.env.shell import shell
from ddev.cli.env.show import show
from ddev.cli.env.start import start
from ddev.cli.env.stop import stop
from ddev.cli.env.test import test_command


@click.group(short_help='Manage environments')
Expand All @@ -20,12 +21,12 @@ def env():
"""


env.add_command(check_run)
env.add_command(edit)
env.add_command(ls)
env.add_command(prune)
env.add_command(reload_env)
env.add_command(agent)
env.add_command(check)
env.add_command(config)
env.add_command(reload_command)
env.add_command(shell)
env.add_command(show)
env.add_command(start)
env.add_command(stop)
env.add_command(test)
env.add_command(test_command)
68 changes: 68 additions & 0 deletions ddev/src/ddev/cli/env/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# (C) Datadog, Inc. 2023-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from __future__ import annotations

from typing import TYPE_CHECKING

import click

if TYPE_CHECKING:
from ddev.cli.application import Application


@click.command(short_help='Invoke the Agent', context_settings={'ignore_unknown_options': True})
@click.argument('intg_name', metavar='INTEGRATION')
@click.argument('environment')
@click.argument('args', required=True, nargs=-1)
@click.option('--config-file', hidden=True)
@click.pass_obj
def agent(app: Application, *, intg_name: str, environment: str, args: tuple[str, ...], config_file: str | None):
"""
Invoke the Agent.
"""
import subprocess

from ddev.e2e.agent import get_agent_interface
from ddev.e2e.config import EnvDataStorage
from ddev.utils.fs import Path

integration = app.repo.integrations.get(intg_name)
env_data = EnvDataStorage(app.data_dir).get(integration.name, environment)

if not env_data.exists():
app.abort(f'Environment `{environment}` for integration `{integration.name}` is not running')

metadata = env_data.read_metadata()
agent_type = metadata['env_type']
agent = get_agent_interface(agent_type)(app.platform, integration, environment, metadata, env_data.config_file)

full_args = list(args)
trigger_run = False
if full_args[0] == 'check':
trigger_run = True

# TODO: remove this when all invocations migrate to the actual command
if len(full_args) > 2 and full_args[1] == '--jmx-list':
full_args = ['jmx', 'list', full_args[2]]
# Automatically inject the integration name if not passed
elif len(full_args) == 1 or full_args[1].startswith('-'):
full_args.insert(1, intg_name)

if config_file is None or not trigger_run:
try:
agent.invoke(full_args)
except subprocess.CalledProcessError as e:
app.abort(code=e.returncode)

return

import json

temp_config_file = env_data.config_file.parent / f'{env_data.config_file.name}.bak.example'
env_data.config_file.replace(temp_config_file)
try:
env_data.write_config(json.loads(Path(config_file).read_text()))
agent.invoke(full_args)
finally:
temp_config_file.replace(env_data.config_file)
18 changes: 18 additions & 0 deletions ddev/src/ddev/cli/env/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# (C) Datadog, Inc. 2023-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from __future__ import annotations

import click


@click.command(context_settings={'ignore_unknown_options': True}, hidden=True)
@click.argument('intg_name')
@click.argument('environment')
@click.argument('args', nargs=-1)
@click.option('--config')
@click.pass_context
def check(ctx: click.Context, *, intg_name: str, environment: str, args: tuple[str, ...], config: str | None):
from ddev.cli.env.agent import agent

ctx.invoke(agent, intg_name=intg_name, environment=environment, args=('check', *args), config_file=config)
95 changes: 95 additions & 0 deletions ddev/src/ddev/cli/env/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# (C) Datadog, Inc. 2023-present
# All rights reserved
# Licensed under a 3-clause BSD style license (see LICENSE)
from __future__ import annotations

from typing import TYPE_CHECKING

import click

if TYPE_CHECKING:
from ddev.cli.application import Application


@click.group(short_help='Manage the config file')
def config():
pass


@config.command(short_help='Edit the config file with your default editor')
@click.argument('intg_name', metavar='INTEGRATION')
@click.argument('environment')
@click.pass_obj
def edit(app: Application, *, intg_name: str, environment: str):
"""
Edit the config file with your default editor.
"""
from ddev.e2e.config import EnvDataStorage

integration = app.repo.integrations.get(intg_name)
env_data = EnvDataStorage(app.data_dir).get(integration.name, environment)

if not env_data.exists():
app.abort(f'Environment `{environment}` for integration `{integration.name}` is not running')

click.edit(filename=str(env_data.config_file))


@config.command(short_help='Open the config location in your file manager')
@click.argument('intg_name', metavar='INTEGRATION')
@click.argument('environment')
@click.pass_obj
def explore(app: Application, *, intg_name: str, environment: str):
"""
Open the config location in your file manager.
"""
from ddev.e2e.config import EnvDataStorage

integration = app.repo.integrations.get(intg_name)
env_data = EnvDataStorage(app.data_dir).get(integration.name, environment)

if not env_data.exists():
app.abort(f'Environment `{environment}` for integration `{integration.name}` is not running')

click.launch(str(env_data.config_file), locate=True)


@config.command(short_help='Show the location of the config file')
@click.argument('intg_name', metavar='INTEGRATION')
@click.argument('environment')
@click.pass_obj
def find(app: Application, *, intg_name: str, environment: str):
"""
Show the location of the config file.
"""
from ddev.e2e.config import EnvDataStorage

integration = app.repo.integrations.get(intg_name)
env_data = EnvDataStorage(app.data_dir).get(integration.name, environment)

if not env_data.exists():
app.abort(f'Environment `{environment}` for integration `{integration.name}` is not running')

app.output(f'[link={env_data.config_file}]{env_data.config_file}[/]')


@config.command(short_help='Show the contents of the config file')
@click.argument('intg_name', metavar='INTEGRATION')
@click.argument('environment')
@click.pass_obj
def show(app: Application, *, intg_name: str, environment: str):
"""
Show the contents of the config file.
"""
from ddev.e2e.config import EnvDataStorage

integration = app.repo.integrations.get(intg_name)
env_data = EnvDataStorage(app.data_dir).get(integration.name, environment)

if not env_data.exists():
app.abort(f'Environment `{environment}` for integration `{integration.name}` is not running')

from rich.syntax import Syntax

text = env_data.config_file.read_text().rstrip()
app.output(Syntax(text, 'yaml', background_color='default'))
Loading

0 comments on commit abc791a

Please sign in to comment.