#1650 Add test_memory
Merged 3 years ago by breilly. Opened 3 years ago by jobrauer.
jobrauer/fm-orchestrator test_stress_memory  into  master

file modified
+8
@@ -9,6 +9,7 @@ 

  import re

  import six

  import time

+ import yaml

  from traceback import extract_stack

  

  import koji
@@ -55,6 +56,13 @@ 

          return to_text_type(mmd.read())

  

  

+ def read_staged_data_as_yaml(yaml_name):

+     filename = staged_data_filename(

+         yaml_name if '.' in yaml_name else "{}.yaml".format(yaml_name))

+     with open(filename, "r") as f:

+         return yaml.safe_load(f)

+ 

+ 

  def patch_config():

      # add test builders for all resolvers

      with_test_builders = dict()

empty or binary file added
@@ -0,0 +1,32 @@ 

+ import os

+ 

+ 

+ class TestConfiguration:

+     # TEST CONFIGURATION ('borrowed' from module_build_service.common.conf)

+     SECRET_KEY = os.urandom(16)

+     SQLALCHEMY_TRACK_MODIFICATIONS = True

+     HOST = "0.0.0.0"

+     PORT = 5000

+ 

+     LOG_LEVEL = "debug"

+     SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URI", "sqlite:///:memory:")

+     DEBUG = True

+     MESSAGING = "in_memory"

+     PDC_URL = "https://pdc.fedoraproject.org/rest_api/v1"

+     NET_TIMEOUT = 10

+     NET_RETRY_INTERVAL = 1

+     SCM_NET_TIMEOUT = 0.1

+     SCM_NET_RETRY_INTERVAL = 0.1

+     KOJI_CONFIG = "./conf/koji.conf"

+     KOJI_PROFILE = "staging"

+     KOJI_REPOSITORY_URL = "https://kojipkgs.stg.fedoraproject.org/repos"

+     SCMURLS = ["https://src.stg.fedoraproject.org/modules/"]

+     ALLOWED_GROUPS_TO_IMPORT_MODULE = {"mbs-import-module"}

+     GREENWAVE_URL = "https://greenwave.example.local/api/v1.0/"

+     GREENWAVE_DECISION_CONTEXT = "test_dec_context"

+     GREENWAVE_SUBJECT_TYPE = "some-module"

+     STREAM_SUFFIXES = {r"^el\d+\.\d+\.\d+\.z$": 0.1}

+     CELERY_TASK_ALWAYS_EAGER = True

+ 

+     NO_AUTH = True

+     YAML_SUBMIT_ALLOWED = True

@@ -0,0 +1,70 @@ 

+ from __future__ import absolute_import, print_function

+ 

+ import koji

+ import mock

+ import signal

+ import threading

+ import types

+ 

+ from module_build_service import app

+ from module_build_service.common import conf

+ from module_build_service.builder import GenericBuilder

+ 

+ from tests.test_build.test_build import FakeModuleBuilder

+ from tests.test_build.test_build import main as run_scheduler

+ 

+ 

+ def patch_config_system_setter():

+     """bypass supported builders check"""

+     def set_system(self, system):

+         self._system = system

+     conf._setifok_system = types.MethodType(set_system, conf)

+ 

+ 

+ def register_fake_builder():

+     patch_config_system_setter()

+     conf.system = FakeModuleBuilder.backend

+     GenericBuilder.register_backend_class(FakeModuleBuilder)

+ 

+     # Builder always instantly succeeds

+     def on_get_task_info_cb(cls, task_id):

+         return {"state": koji.TASK_STATES["CLOSED"]}

+     FakeModuleBuilder.on_get_task_info_cb = on_get_task_info_cb

+ 

+ 

+ class SimpleMock:

+     """Dummy callable mock object - we want our memory footprint to be as small as possible"""

+     def __call__(self, *args, **kwargs):

+         return True

+ 

+ 

+ @mock.patch("module_build_service.scheduler.handlers.modules.handle_stream_collision_modules",

+             new_callable=SimpleMock)

+ @mock.patch("module_build_service.scheduler.handlers.modules.record_module_build_arches",

+             new_callable=SimpleMock)

+ @mock.patch("module_build_service.scheduler.greenwave.Greenwave.check_gating",

+             new_callable=SimpleMock)

+ def run_debug_instance(mock_1, mock_2, mock_3, host=None, port=None):

+ 

+     def handle_pdb(sig, frame):

+         import pdb

+         pdb.Pdb().set_trace(frame)

+ 

+     # kill -10 <PID> to start debugger

+     signal.signal(signal.SIGUSR1, handle_pdb)

Having some trouble following along this part - why is pdb being used here?

+ 

+     register_fake_builder()

+ 

+     host = host or conf.host

+     port = port or conf.port

+ 

+     def run_app():

+         app.run(host=host, port=port, debug=False)

+     threading.Thread(target=run_app, daemon=True).start()

+ 

+     # run moksha hub and never stop

+     run_scheduler([], stop_condition=lambda msg: False)

+ 

+ 

+ if __name__ == '__main__':

+     run_debug_instance()

@@ -0,0 +1,103 @@ 

+ import json

+ import os

+ import psutil

+ import pytest

+ import requests

+ import time

+ 

+ from subprocess import Popen

+ from tests import read_staged_data_as_yaml

+ 

+ 

+ base_dir = os.path.dirname(__file__)

+ yaml = read_staged_data_as_yaml("testmodule.yaml")

+ 

+ LOCAL_MBS_URL = "http://localhost:5000/module-build-service/1/module-builds/"

+ 

+ 

+ def submit_yaml_build(module_name):

+     """Submit module build with custom name to get a unique NVR each time."""

+     yaml["data"]["name"] = module_name

+     data = {"modulemd": str(yaml), "module_name": "testmodule"}

+     r = requests.post(LOCAL_MBS_URL, data=json.dumps(data))

+     if r.status_code > 300:

+         pytest.fail(str(r.json()))

+     return r.json()["id"]

+ 

+ 

+ def wait_for_module_build(build_id, timeout=60, interval=5):

+     """Wait for module build to be ready

+ 

+     :param int build_id: build definition (either id or Build object)

+     :param float timeout: timeout in seconds

+     :param float interval: scan interval in seconds

+     """

+     start = time.time()

+ 

+     while (time.time() - start) < timeout:

+         state = requests.get(LOCAL_MBS_URL + str(build_id)).json()["state_name"]

+         if state == "ready":

+             return

+         time.sleep(interval)

+     pytest.skip("Wait for build timed out after {}s".format(timeout))

+ 

+ 

+ @pytest.fixture()

+ def run_debug_mbs_instance():

+     """Starts a 'debug' MBS instance:

+     * mbs-frontend (no auth, yaml import enabled)

+     * moksha hub (in memory messaging)

+     * tests.test_build.test_build.FakeModuleBuilder as builder backend (always succeeds)

+ 

+     Optionally:

+     Set MBS_TEST_INSTANCE_PID env variable to run test against your own running instance.

+     If you intend to run a standalone instance, make sure you have these env vars set:

+     * MODULE_BUILD_SERVICE_DEVELOPER_ENV=0

+     * MBS_CONFIG_SECTION=TestConfiguration

+     * MBS_CONFIG_FILE=tests/test_memory/mbs_configuration.py

+     * DATABASE_URI=postgresql+psycopg2://postgres:@127.0.0.1/mbstest

+ 

+     ...then 'python tests/test_memory/mbs_debug.py' (and 'kill -10 <PID>' to start debugger)

+     """

+ 

+     process = None

+     try:

+         running_instance_pid = int(os.environ.get("MBS_TEST_INSTANCE_PID"))

+         yield running_instance_pid

+     except TypeError or ValueError:

+         mbs_config_file_path = os.path.join(base_dir, "mbs_configuration.py")

+         env = {

+             "MBS_CONFIG_SECTION": "TestConfiguration",

+             "MBS_CONFIG_FILE": mbs_config_file_path,

+         }

+         # Pass the preset database configuration (if present)

+         if os.environ.get("DATABASE_URI"):

+             env["DATABASE_URI"] = os.environ.get("DATABASE_URI")

+ 

+         mbs_exec_script = os.path.join(base_dir, "mbs_debug.py")

+         process = Popen(["python", mbs_exec_script], stdin=None, stdout=None, env=env)

+         time.sleep(5)  # wait a couple of secs for MBS to start

+         yield process.pid

+     if process:

+         process.terminate()

+ 

+ 

+ @pytest.mark.parametrize("num_builds", [20])

+ def test_submit_build(require_platform_and_default_arch, run_debug_mbs_instance, num_builds):

+     pid = run_debug_mbs_instance

+     process = psutil.Process(pid)

+ 

+     def get_rss():  # resident set size in MB

+         return process.memory_info().rss / 1000000

+     consumed_memory = []

+ 

+     for i in range(num_builds):

+         build_id = submit_yaml_build("test-module-{}".format(i))

+         # wait for the build to finish, so that the build logger is flushed/closed

+         wait_for_module_build(build_id, interval=0.5, timeout=10)

+         consumed_memory.append(get_rss())

+ 

+     print("Memory [MB]: {}".format(consumed_memory))

+ 

+     if (consumed_memory[-1] - consumed_memory[0]) > 0.1:

+         pytest.fail("Memory is leaking, [MB]: {}".format(consumed_memory))

file modified
+10
@@ -23,6 +23,7 @@ 

  commands =

      py.test -v \

          --ignore tests/integration \

+         --ignore tests/test_memory \

          --cov module_build_service \

          --cov-report html \

          --cov-report term \
@@ -91,3 +92,12 @@ 

          --html=report.html \

          --self-contained-html \

          {posargs:tests/integration}

+ 

+ [testenv:memory]

+ basepython = python3

+ deps = {[testenv]deps}

+ passenv =

+     MBS_TEST_INSTANCE_PID

+     DATABASE_URI

+ commands =

+     py.test -rA {posargs:tests/test_memory --show-capture=no}

  • Add a script that runs test MBS instance.
  • Add memory tracking test-case.
  • New tox environment: run with 'tox -e memory'.

pretty please pagure-ci rebuild

3 years ago

rebased onto 6400b64

3 years ago

Having some trouble following along this part - why is pdb being used here?

@breilly since this test is supposed to be run manually (or at least I thought so), I used pdb here to make debugging a bit easier - just sending a signal. I don't know if this is a correct use, I can of course remove this "manual" aspect of the test.

Ah I see, makes sense. Change LGTM.

Commit 107fe1c fixes this pull-request

Pull-Request has been merged by breilly

3 years ago

Pull-Request has been merged by breilly

3 years ago