From f7e96450678cf231f52113a20cc571e499b6fa6c Mon Sep 17 00:00:00 2001 From: ali ugur Date: Fri, 17 Jan 2025 07:01:50 +0300 Subject: [PATCH] chore(test): Improve tests --- .trivyignore | 6 +-- .../integration/integrations/test_tracing.py | 6 +-- tests/unit/flask/constants.py | 27 +++++++++++++ tests/unit/flask/test_tracing.py | 19 +++++++++ tests/unit/flask/test_webserver.py | 39 +++++++++++++++++-- 5 files changed, 85 insertions(+), 12 deletions(-) diff --git a/.trivyignore b/.trivyignore index 6c2a020..b88a77b 100644 --- a/.trivyignore +++ b/.trivyignore @@ -1,10 +1,6 @@ -# ignore CVE introduced by python3-gunicorn -CVE-2022-40897 # pypa/setuptools: Remote code execution via download CVE-2024-6345 # pebble: Calling Decoder.Decode on a message which contains deeply nested structures can cause a panic due to stack exhaustion CVE-2024-34156 -# pebble: Go stdlib -CVE-2024-45338 # go-app: Go crypto lib -CVE-2024-45337 +CVE-2024-45337 diff --git a/tests/integration/integrations/test_tracing.py b/tests/integration/integrations/test_tracing.py index 9db8ae9..d912085 100644 --- a/tests/integration/integrations/test_tracing.py +++ b/tests/integration/integrations/test_tracing.py @@ -20,20 +20,19 @@ @pytest.mark.parametrize( - "tracing_app, port", + "tracing_app_fixture, port", [ ("flask_tracing_app", 8000), ("django_tracing_app", 8000), ("fastapi_tracing_app", 8080), ("go_tracing_app", 8080), ], - indirect=["tracing_app"], ) @pytest.mark.skip_juju_version("3.4") # Tempo only supports Juju>=3.4 async def test_workload_tracing( ops_test: OpsTest, model: Model, - tracing_app: Application, + tracing_app_fixture: str, port: int, request: pytest.FixtureRequest, get_unit_ips, @@ -49,6 +48,7 @@ async def test_workload_tracing( except Exception as e: logger.info(f"Tempo is already deployed {e}") + tracing_app = request.getfixturevalue(tracing_app_fixture) idle_list = [tracing_app.name] if tracing_app.name != "flask-tracing-k8s": diff --git a/tests/unit/flask/constants.py b/tests/unit/flask/constants.py index 285ea32..af6712c 100644 --- a/tests/unit/flask/constants.py +++ b/tests/unit/flask/constants.py @@ -26,6 +26,33 @@ } } +LAYER_WITH_TRACING = { + "services": { + "flask": { + "override": "replace", + "startup": "enabled", + "command": f"/bin/python3 -m gunicorn -c /flask/gunicorn.conf.py app:app -k sync", + "after": ["statsd-exporter"], + "user": "_daemon_", + "environment": { + "OTEL_EXPORTER_OTLP_ENDPOINT": "http://test-ip:4318", + "OTEL_SERVICE_NAME": "flask-k8s", + }, + }, + "statsd-exporter": { + "override": "merge", + "command": ( + "/bin/statsd_exporter --statsd.mapping-config=/statsd-mapping.conf " + "--statsd.listen-udp=localhost:9125 " + "--statsd.listen-tcp=localhost:9125" + ), + "summary": "statsd exporter service", + "startup": "enabled", + "user": "_daemon_", + }, + } +} + LAYER_WITH_WORKER = { "services": { "flask": { diff --git a/tests/unit/flask/test_tracing.py b/tests/unit/flask/test_tracing.py index 126db90..ecc6976 100644 --- a/tests/unit/flask/test_tracing.py +++ b/tests/unit/flask/test_tracing.py @@ -36,3 +36,22 @@ def test_tracing_relation(harness: Harness): service_env = container.get_plan().services["flask"].environment assert service_env["OTEL_EXPORTER_OTLP_ENDPOINT"] == "http://test-ip:4318" assert service_env["OTEL_SERVICE_NAME"] == "flask-k8s" + + +def test_tracing_not_activated(harness: Harness): + """ + arrange: Deploy the charm without a relation to the Tempo charm. + act: Run all initial hooks. + assert: The flask service should not have the environment variables OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_SERVICE_NAME. + """ + harness.set_model_name("flask-model") + + container = harness.model.unit.get_container(FLASK_CONTAINER_NAME) + container.add_layer("a_layer", DEFAULT_LAYER) + + harness.begin_with_initial_hooks() + + assert harness.model.unit.status == ops.ActiveStatus() + service_env = container.get_plan().services["flask"].environment + assert service_env.get("OTEL_EXPORTER_OTLP_ENDPOINT", None) is None + assert service_env.get("OTEL_SERVICE_NAME", None) is None diff --git a/tests/unit/flask/test_webserver.py b/tests/unit/flask/test_webserver.py index 3117a7a..5652484 100644 --- a/tests/unit/flask/test_webserver.py +++ b/tests/unit/flask/test_webserver.py @@ -19,11 +19,12 @@ from paas_charm.charm_state import CharmState from paas_charm.utils import enable_pebble_log_forwarding -from .constants import DEFAULT_LAYER, FLASK_CONTAINER_NAME +from .constants import DEFAULT_LAYER, FLASK_CONTAINER_NAME, LAYER_WITH_TRACING GUNICORN_CONFIG_TEST_PARAMS = [ pytest.param( {"workers": 10}, + DEFAULT_LAYER, textwrap.dedent( f"""\ bind = ['0.0.0.0:8000'] @@ -37,6 +38,7 @@ ), pytest.param( {"threads": 2, "timeout": 3, "keepalive": 4}, + DEFAULT_LAYER, textwrap.dedent( f"""\ bind = ['0.0.0.0:8000'] @@ -50,12 +52,41 @@ ), id="threads=2,timeout=3,keepalive=4", ), + pytest.param( + {}, + LAYER_WITH_TRACING, + textwrap.dedent( + f"""\ + bind = ['0.0.0.0:8000'] + chdir = '/flask/app' + accesslog = '/var/log/flask/access.log' + errorlog = '/var/log/flask/error.log' + statsd_host = 'localhost:9125' + from opentelemetry import trace + from opentelemetry.exporter.otlp.proto.http.trace_exporter import ( + OTLPSpanExporter, + ) + from opentelemetry.sdk.trace import TracerProvider + from opentelemetry.sdk.trace.export import BatchSpanProcessor + + def post_fork(server, worker): + trace.set_tracer_provider(TracerProvider()) + span_processor = BatchSpanProcessor(OTLPSpanExporter()) + trace.get_tracer_provider().add_span_processor(span_processor) + """ + ), + id="with-tracing", + ), ] -@pytest.mark.parametrize("charm_state_params, config_file", GUNICORN_CONFIG_TEST_PARAMS) +@pytest.mark.parametrize("charm_state_params, layer, config_file", GUNICORN_CONFIG_TEST_PARAMS) def test_gunicorn_config( - harness: Harness, charm_state_params, config_file, database_migration_mock + harness: Harness, + charm_state_params, + layer, + config_file, + database_migration_mock, ) -> None: """ arrange: create the Gunicorn webserver object with a controlled charm state generated by the @@ -66,7 +97,7 @@ def test_gunicorn_config( harness.begin() container: ops.Container = harness.model.unit.get_container(FLASK_CONTAINER_NAME) harness.set_can_connect(FLASK_CONTAINER_NAME, True) - container.add_layer("default", DEFAULT_LAYER) + container.add_layer("default", layer) charm_state = CharmState( framework="flask",