1516Szelenkov@nginx.comimport re 21926Smax.romanov@nginx.comimport shutil 31477Szelenkov@nginx.comimport subprocess 4507Smax.romanov@nginx.comimport time 51477Szelenkov@nginx.com 61635Szelenkov@nginx.comimport pytest 71019Szelenkov@nginx.comfrom unit.applications.lang.python import TestApplicationPython 81730Szelenkov@nginx.comfrom unit.option import option 9507Smax.romanov@nginx.com 101017Szelenkov@nginx.com 111019Szelenkov@nginx.comclass TestPythonProcman(TestApplicationPython): 121467Szelenkov@nginx.com prerequisites = {'modules': {'python': 'any'}} 13507Smax.romanov@nginx.com 14*2478Szelenkov@nginx.com @pytest.fixture(autouse=True) 15*2478Szelenkov@nginx.com def setup_method_fixture(self, temp_dir): 16*2478Szelenkov@nginx.com self.app_name = f'app-{temp_dir.split("/")[-1]}' 172330Szelenkov@nginx.com self.app_proc = f'applications/{self.app_name}/processes' 181416Szelenkov@nginx.com self.load('empty', self.app_name) 191416Szelenkov@nginx.com 20552Szelenkov@nginx.com def pids_for_process(self): 21516Szelenkov@nginx.com time.sleep(0.2) 22516Szelenkov@nginx.com 23516Szelenkov@nginx.com output = subprocess.check_output(['ps', 'ax']) 24507Smax.romanov@nginx.com 25516Szelenkov@nginx.com pids = set() 261999Smax.romanov@nginx.com for m in re.findall( 272330Szelenkov@nginx.com fr'.*unit: "{self.app_name}" application', output.decode() 281999Smax.romanov@nginx.com ): 291596Szelenkov@nginx.com pids.add(re.search(r'^\s*(\d+)', m).group(1)) 30507Smax.romanov@nginx.com 31516Szelenkov@nginx.com return pids 32507Smax.romanov@nginx.com 331416Szelenkov@nginx.com def conf_proc(self, conf, path=None): 341416Szelenkov@nginx.com if path is None: 351416Szelenkov@nginx.com path = self.app_proc 36555Szelenkov@nginx.com 371596Szelenkov@nginx.com assert 'success' in self.conf(conf, path), 'configure processes' 38517Szelenkov@nginx.com 391596Szelenkov@nginx.com @pytest.mark.skip('not yet') 40517Szelenkov@nginx.com def test_python_processes_idle_timeout_zero(self): 411416Szelenkov@nginx.com self.conf_proc({"spare": 0, "max": 2, "idle_timeout": 0}) 42517Szelenkov@nginx.com 43517Szelenkov@nginx.com self.get() 441596Szelenkov@nginx.com assert len(self.pids_for_process()) == 0, 'idle timeout 0' 45517Szelenkov@nginx.com 46516Szelenkov@nginx.com def test_python_prefork(self): 471416Szelenkov@nginx.com self.conf_proc('2') 48516Szelenkov@nginx.com 49516Szelenkov@nginx.com pids = self.pids_for_process() 501596Szelenkov@nginx.com assert len(pids) == 2, 'prefork 2' 51516Szelenkov@nginx.com 52516Szelenkov@nginx.com self.get() 531596Szelenkov@nginx.com assert self.pids_for_process() == pids, 'prefork still 2' 54516Szelenkov@nginx.com 551416Szelenkov@nginx.com self.conf_proc('4') 56516Szelenkov@nginx.com 57516Szelenkov@nginx.com pids = self.pids_for_process() 581596Szelenkov@nginx.com assert len(pids) == 4, 'prefork 4' 59507Smax.romanov@nginx.com 60507Smax.romanov@nginx.com self.get() 611596Szelenkov@nginx.com assert self.pids_for_process() == pids, 'prefork still 4' 62516Szelenkov@nginx.com 63516Szelenkov@nginx.com self.stop_all() 64516Szelenkov@nginx.com 651596Szelenkov@nginx.com @pytest.mark.skip('not yet') 66517Szelenkov@nginx.com def test_python_prefork_same_processes(self): 671416Szelenkov@nginx.com self.conf_proc('2') 68517Szelenkov@nginx.com pids = self.pids_for_process() 69517Szelenkov@nginx.com 701416Szelenkov@nginx.com self.conf_proc('4') 71517Szelenkov@nginx.com pids_new = self.pids_for_process() 72517Szelenkov@nginx.com 731596Szelenkov@nginx.com assert pids.issubset(pids_new), 'prefork same processes' 74517Szelenkov@nginx.com 75516Szelenkov@nginx.com def test_python_ondemand(self): 761416Szelenkov@nginx.com self.conf_proc({"spare": 0, "max": 8, "idle_timeout": 1}) 77516Szelenkov@nginx.com 781596Szelenkov@nginx.com assert len(self.pids_for_process()) == 0, 'on-demand 0' 79507Smax.romanov@nginx.com 80507Smax.romanov@nginx.com self.get() 81516Szelenkov@nginx.com pids = self.pids_for_process() 821596Szelenkov@nginx.com assert len(pids) == 1, 'on-demand 1' 83507Smax.romanov@nginx.com 84516Szelenkov@nginx.com self.get() 851596Szelenkov@nginx.com assert self.pids_for_process() == pids, 'on-demand still 1' 86516Szelenkov@nginx.com 87516Szelenkov@nginx.com time.sleep(1) 88516Szelenkov@nginx.com 891596Szelenkov@nginx.com assert len(self.pids_for_process()) == 0, 'on-demand stop idle' 90507Smax.romanov@nginx.com 91515Szelenkov@nginx.com self.stop_all() 92507Smax.romanov@nginx.com 93507Smax.romanov@nginx.com def test_python_scale_updown(self): 941416Szelenkov@nginx.com self.conf_proc({"spare": 2, "max": 8, "idle_timeout": 1}) 95507Smax.romanov@nginx.com 96516Szelenkov@nginx.com pids = self.pids_for_process() 971596Szelenkov@nginx.com assert len(pids) == 2, 'updown 2' 98507Smax.romanov@nginx.com 99507Smax.romanov@nginx.com self.get() 100516Szelenkov@nginx.com pids_new = self.pids_for_process() 1011596Szelenkov@nginx.com assert len(pids_new) == 3, 'updown 3' 1021596Szelenkov@nginx.com assert pids.issubset(pids_new), 'updown 3 only 1 new' 103507Smax.romanov@nginx.com 104507Smax.romanov@nginx.com self.get() 1051596Szelenkov@nginx.com assert self.pids_for_process() == pids_new, 'updown still 3' 106507Smax.romanov@nginx.com 107516Szelenkov@nginx.com time.sleep(1) 108516Szelenkov@nginx.com 109516Szelenkov@nginx.com pids = self.pids_for_process() 1101596Szelenkov@nginx.com assert len(pids) == 2, 'updown stop idle' 111507Smax.romanov@nginx.com 112507Smax.romanov@nginx.com self.get() 113516Szelenkov@nginx.com pids_new = self.pids_for_process() 1141596Szelenkov@nginx.com assert len(pids_new) == 3, 'updown again 3' 1151596Szelenkov@nginx.com assert pids.issubset(pids_new), 'updown again 3 only 1 new' 116507Smax.romanov@nginx.com 117515Szelenkov@nginx.com self.stop_all() 118507Smax.romanov@nginx.com 119507Smax.romanov@nginx.com def test_python_reconfigure(self): 1201416Szelenkov@nginx.com self.conf_proc({"spare": 2, "max": 6, "idle_timeout": 1}) 121507Smax.romanov@nginx.com 122516Szelenkov@nginx.com pids = self.pids_for_process() 1231596Szelenkov@nginx.com assert len(pids) == 2, 'reconf 2' 124507Smax.romanov@nginx.com 125507Smax.romanov@nginx.com self.get() 126516Szelenkov@nginx.com pids_new = self.pids_for_process() 1271596Szelenkov@nginx.com assert len(pids_new) == 3, 'reconf 3' 1281596Szelenkov@nginx.com assert pids.issubset(pids_new), 'reconf 3 only 1 new' 129507Smax.romanov@nginx.com 1302330Szelenkov@nginx.com self.conf_proc('6', f'{self.app_proc}/spare') 131515Szelenkov@nginx.com 132516Szelenkov@nginx.com pids = self.pids_for_process() 1331596Szelenkov@nginx.com assert len(pids) == 6, 'reconf 6' 134507Smax.romanov@nginx.com 135507Smax.romanov@nginx.com self.get() 1361596Szelenkov@nginx.com assert self.pids_for_process() == pids, 'reconf still 6' 137507Smax.romanov@nginx.com 138515Szelenkov@nginx.com self.stop_all() 139515Szelenkov@nginx.com 140517Szelenkov@nginx.com def test_python_idle_timeout(self): 1411416Szelenkov@nginx.com self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2}) 142517Szelenkov@nginx.com 143517Szelenkov@nginx.com self.get() 144517Szelenkov@nginx.com pids = self.pids_for_process() 1451596Szelenkov@nginx.com assert len(pids) == 1, 'idle timeout 1' 146517Szelenkov@nginx.com 147517Szelenkov@nginx.com time.sleep(1) 148517Szelenkov@nginx.com 149517Szelenkov@nginx.com self.get() 150517Szelenkov@nginx.com 151517Szelenkov@nginx.com time.sleep(1) 152517Szelenkov@nginx.com 153517Szelenkov@nginx.com pids_new = self.pids_for_process() 1541596Szelenkov@nginx.com assert len(pids_new) == 1, 'idle timeout still 1' 1551596Szelenkov@nginx.com assert self.pids_for_process() == pids, 'idle timeout still 1 same pid' 156517Szelenkov@nginx.com 157517Szelenkov@nginx.com time.sleep(1) 158517Szelenkov@nginx.com 1591596Szelenkov@nginx.com assert len(self.pids_for_process()) == 0, 'idle timed out' 160517Szelenkov@nginx.com 161517Szelenkov@nginx.com def test_python_processes_connection_keepalive(self): 1621416Szelenkov@nginx.com self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2}) 163517Szelenkov@nginx.com 1642477Szelenkov@nginx.com (_, sock) = self.get( 1651017Szelenkov@nginx.com headers={'Host': 'localhost', 'Connection': 'keep-alive'}, 1661017Szelenkov@nginx.com start=True, 1671017Szelenkov@nginx.com read_timeout=1, 1681017Szelenkov@nginx.com ) 1691596Szelenkov@nginx.com assert len(self.pids_for_process()) == 1, 'keepalive connection 1' 170517Szelenkov@nginx.com 171517Szelenkov@nginx.com time.sleep(2) 172517Szelenkov@nginx.com 1731596Szelenkov@nginx.com assert len(self.pids_for_process()) == 0, 'keepalive connection 0' 174517Szelenkov@nginx.com 175517Szelenkov@nginx.com sock.close() 176517Szelenkov@nginx.com 1771416Szelenkov@nginx.com def test_python_processes_access(self): 1781416Szelenkov@nginx.com self.conf_proc('1') 1791416Szelenkov@nginx.com 1802330Szelenkov@nginx.com path = f'/{self.app_proc}' 1812330Szelenkov@nginx.com assert 'error' in self.conf_get(f'{path}/max') 1822330Szelenkov@nginx.com assert 'error' in self.conf_get(f'{path}/spare') 1832330Szelenkov@nginx.com assert 'error' in self.conf_get(f'{path}/idle_timeout') 1841416Szelenkov@nginx.com 1851416Szelenkov@nginx.com def test_python_processes_invalid(self): 1861596Szelenkov@nginx.com assert 'error' in self.conf( 1871596Szelenkov@nginx.com {"spare": -1}, self.app_proc 1881596Szelenkov@nginx.com ), 'negative spare' 1891596Szelenkov@nginx.com assert 'error' in self.conf({"max": -1}, self.app_proc), 'negative max' 1901596Szelenkov@nginx.com assert 'error' in self.conf( 1911596Szelenkov@nginx.com {"idle_timeout": -1}, self.app_proc 1921596Szelenkov@nginx.com ), 'negative idle_timeout' 1931596Szelenkov@nginx.com assert 'error' in self.conf( 1941596Szelenkov@nginx.com {"spare": 2}, self.app_proc 1951596Szelenkov@nginx.com ), 'spare gt max default' 1961596Szelenkov@nginx.com assert 'error' in self.conf( 1971596Szelenkov@nginx.com {"spare": 2, "max": 1}, self.app_proc 1981596Szelenkov@nginx.com ), 'spare gt max' 1991596Szelenkov@nginx.com assert 'error' in self.conf( 2001596Szelenkov@nginx.com {"spare": 0, "max": 0}, self.app_proc 2011596Szelenkov@nginx.com ), 'max zero' 2021416Szelenkov@nginx.com 203515Szelenkov@nginx.com def stop_all(self): 2041775Szelenkov@nginx.com assert 'success' in self.conf({"listeners": {}, "applications": {}}) 205507Smax.romanov@nginx.com 2061596Szelenkov@nginx.com assert len(self.pids_for_process()) == 0, 'stop all' 2071926Smax.romanov@nginx.com 2081926Smax.romanov@nginx.com def test_python_restart(self, temp_dir): 2091926Smax.romanov@nginx.com shutil.copyfile( 2102330Szelenkov@nginx.com f'{option.test_dir}/python/restart/v1.py', f'{temp_dir}/wsgi.py' 2111926Smax.romanov@nginx.com ) 2121926Smax.romanov@nginx.com 2131926Smax.romanov@nginx.com self.load( 2141926Smax.romanov@nginx.com temp_dir, 2151926Smax.romanov@nginx.com name=self.app_name, 2161926Smax.romanov@nginx.com processes=1, 2171926Smax.romanov@nginx.com environment={'PYTHONDONTWRITEBYTECODE': '1'}, 2181926Smax.romanov@nginx.com ) 2191926Smax.romanov@nginx.com 2201926Smax.romanov@nginx.com b = self.get()['body'] 2211926Smax.romanov@nginx.com assert b == "v1", 'process started' 2221926Smax.romanov@nginx.com 2231926Smax.romanov@nginx.com shutil.copyfile( 2242330Szelenkov@nginx.com f'{option.test_dir}/python/restart/v2.py', f'{temp_dir}/wsgi.py' 2251926Smax.romanov@nginx.com ) 2261926Smax.romanov@nginx.com 2271926Smax.romanov@nginx.com b = self.get()['body'] 2281926Smax.romanov@nginx.com assert b == "v1", 'still old process' 2291926Smax.romanov@nginx.com 2301926Smax.romanov@nginx.com assert 'success' in self.conf_get( 2312330Szelenkov@nginx.com f'/control/applications/{self.app_name}/restart' 2321926Smax.romanov@nginx.com ), 'restart processes' 2331926Smax.romanov@nginx.com 2341926Smax.romanov@nginx.com b = self.get()['body'] 2351926Smax.romanov@nginx.com assert b == "v2", 'new process started' 2361926Smax.romanov@nginx.com 2371926Smax.romanov@nginx.com assert 'error' in self.conf_get( 2381926Smax.romanov@nginx.com '/control/applications/blah/restart' 2391926Smax.romanov@nginx.com ), 'application incorrect' 2401926Smax.romanov@nginx.com 2411926Smax.romanov@nginx.com assert 'error' in self.conf_delete( 2422330Szelenkov@nginx.com f'/control/applications/{self.app_name}/restart' 2431926Smax.romanov@nginx.com ), 'method incorrect' 2441926Smax.romanov@nginx.com 2451926Smax.romanov@nginx.com def test_python_restart_multi(self): 2461926Smax.romanov@nginx.com self.conf_proc('2') 2471926Smax.romanov@nginx.com 2481926Smax.romanov@nginx.com pids = self.pids_for_process() 2491926Smax.romanov@nginx.com assert len(pids) == 2, 'restart 2 started' 2501926Smax.romanov@nginx.com 2511926Smax.romanov@nginx.com assert 'success' in self.conf_get( 2522330Szelenkov@nginx.com f'/control/applications/{self.app_name}/restart' 2531926Smax.romanov@nginx.com ), 'restart processes' 2541926Smax.romanov@nginx.com 2551926Smax.romanov@nginx.com new_pids = self.pids_for_process() 2561926Smax.romanov@nginx.com assert len(new_pids) == 2, 'restart still 2' 2571926Smax.romanov@nginx.com 2581926Smax.romanov@nginx.com assert len(new_pids.intersection(pids)) == 0, 'restart all new' 2591926Smax.romanov@nginx.com 2601926Smax.romanov@nginx.com def test_python_restart_longstart(self): 2611926Smax.romanov@nginx.com self.load( 2621926Smax.romanov@nginx.com 'restart', 2631926Smax.romanov@nginx.com name=self.app_name, 2641926Smax.romanov@nginx.com module="longstart", 2651926Smax.romanov@nginx.com processes={"spare": 1, "max": 2, "idle_timeout": 5}, 2661926Smax.romanov@nginx.com ) 2671926Smax.romanov@nginx.com 2681926Smax.romanov@nginx.com assert len(self.pids_for_process()) == 1, 'longstarts == 1' 2691926Smax.romanov@nginx.com 2701971Szelenkov@nginx.com self.get() 2711971Szelenkov@nginx.com 2721926Smax.romanov@nginx.com pids = self.pids_for_process() 2731926Smax.romanov@nginx.com assert len(pids) == 2, 'longstarts == 2' 2741926Smax.romanov@nginx.com 2751926Smax.romanov@nginx.com assert 'success' in self.conf_get( 2762330Szelenkov@nginx.com f'/control/applications/{self.app_name}/restart' 2771926Smax.romanov@nginx.com ), 'restart processes' 2781926Smax.romanov@nginx.com 2791926Smax.romanov@nginx.com # wait for longstarted app 2801926Smax.romanov@nginx.com time.sleep(2) 2811926Smax.romanov@nginx.com 2821926Smax.romanov@nginx.com new_pids = self.pids_for_process() 2831926Smax.romanov@nginx.com assert len(new_pids) == 1, 'restart 1' 2841926Smax.romanov@nginx.com 2851926Smax.romanov@nginx.com assert len(new_pids.intersection(pids)) == 0, 'restart all new' 286