xref: /unit/test/test_python_procman.py (revision 1999:00d43b03d82f)
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
141596Szelenkov@nginx.com    def setup_method(self):
151654Szelenkov@nginx.com        self.app_name = "app-" + option.temp_dir.split('/')[-1]
161416Szelenkov@nginx.com        self.app_proc = 'applications/' + self.app_name + '/processes'
171416Szelenkov@nginx.com        self.load('empty', self.app_name)
181416Szelenkov@nginx.com
19552Szelenkov@nginx.com    def pids_for_process(self):
20516Szelenkov@nginx.com        time.sleep(0.2)
21516Szelenkov@nginx.com
22516Szelenkov@nginx.com        output = subprocess.check_output(['ps', 'ax'])
23507Smax.romanov@nginx.com
24516Szelenkov@nginx.com        pids = set()
25*1999Smax.romanov@nginx.com        for m in re.findall(
26*1999Smax.romanov@nginx.com            '.*unit: "' + self.app_name + '" application', output.decode()
27*1999Smax.romanov@nginx.com        ):
281596Szelenkov@nginx.com            pids.add(re.search(r'^\s*(\d+)', m).group(1))
29507Smax.romanov@nginx.com
30516Szelenkov@nginx.com        return pids
31507Smax.romanov@nginx.com
321416Szelenkov@nginx.com    def conf_proc(self, conf, path=None):
331416Szelenkov@nginx.com        if path is None:
341416Szelenkov@nginx.com            path = self.app_proc
35555Szelenkov@nginx.com
361596Szelenkov@nginx.com        assert 'success' in self.conf(conf, path), 'configure processes'
37517Szelenkov@nginx.com
381596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
39517Szelenkov@nginx.com    def test_python_processes_idle_timeout_zero(self):
401416Szelenkov@nginx.com        self.conf_proc({"spare": 0, "max": 2, "idle_timeout": 0})
41517Szelenkov@nginx.com
42517Szelenkov@nginx.com        self.get()
431596Szelenkov@nginx.com        assert len(self.pids_for_process()) == 0, 'idle timeout 0'
44517Szelenkov@nginx.com
45516Szelenkov@nginx.com    def test_python_prefork(self):
461416Szelenkov@nginx.com        self.conf_proc('2')
47516Szelenkov@nginx.com
48516Szelenkov@nginx.com        pids = self.pids_for_process()
491596Szelenkov@nginx.com        assert len(pids) == 2, 'prefork 2'
50516Szelenkov@nginx.com
51516Szelenkov@nginx.com        self.get()
521596Szelenkov@nginx.com        assert self.pids_for_process() == pids, 'prefork still 2'
53516Szelenkov@nginx.com
541416Szelenkov@nginx.com        self.conf_proc('4')
55516Szelenkov@nginx.com
56516Szelenkov@nginx.com        pids = self.pids_for_process()
571596Szelenkov@nginx.com        assert len(pids) == 4, 'prefork 4'
58507Smax.romanov@nginx.com
59507Smax.romanov@nginx.com        self.get()
601596Szelenkov@nginx.com        assert self.pids_for_process() == pids, 'prefork still 4'
61516Szelenkov@nginx.com
62516Szelenkov@nginx.com        self.stop_all()
63516Szelenkov@nginx.com
641596Szelenkov@nginx.com    @pytest.mark.skip('not yet')
65517Szelenkov@nginx.com    def test_python_prefork_same_processes(self):
661416Szelenkov@nginx.com        self.conf_proc('2')
67517Szelenkov@nginx.com        pids = self.pids_for_process()
68517Szelenkov@nginx.com
691416Szelenkov@nginx.com        self.conf_proc('4')
70517Szelenkov@nginx.com        pids_new = self.pids_for_process()
71517Szelenkov@nginx.com
721596Szelenkov@nginx.com        assert pids.issubset(pids_new), 'prefork same processes'
73517Szelenkov@nginx.com
74516Szelenkov@nginx.com    def test_python_ondemand(self):
751416Szelenkov@nginx.com        self.conf_proc({"spare": 0, "max": 8, "idle_timeout": 1})
76516Szelenkov@nginx.com
771596Szelenkov@nginx.com        assert len(self.pids_for_process()) == 0, 'on-demand 0'
78507Smax.romanov@nginx.com
79507Smax.romanov@nginx.com        self.get()
80516Szelenkov@nginx.com        pids = self.pids_for_process()
811596Szelenkov@nginx.com        assert len(pids) == 1, 'on-demand 1'
82507Smax.romanov@nginx.com
83516Szelenkov@nginx.com        self.get()
841596Szelenkov@nginx.com        assert self.pids_for_process() == pids, 'on-demand still 1'
85516Szelenkov@nginx.com
86516Szelenkov@nginx.com        time.sleep(1)
87516Szelenkov@nginx.com
881596Szelenkov@nginx.com        assert len(self.pids_for_process()) == 0, 'on-demand stop idle'
89507Smax.romanov@nginx.com
90515Szelenkov@nginx.com        self.stop_all()
91507Smax.romanov@nginx.com
92507Smax.romanov@nginx.com    def test_python_scale_updown(self):
931416Szelenkov@nginx.com        self.conf_proc({"spare": 2, "max": 8, "idle_timeout": 1})
94507Smax.romanov@nginx.com
95516Szelenkov@nginx.com        pids = self.pids_for_process()
961596Szelenkov@nginx.com        assert len(pids) == 2, 'updown 2'
97507Smax.romanov@nginx.com
98507Smax.romanov@nginx.com        self.get()
99516Szelenkov@nginx.com        pids_new = self.pids_for_process()
1001596Szelenkov@nginx.com        assert len(pids_new) == 3, 'updown 3'
1011596Szelenkov@nginx.com        assert pids.issubset(pids_new), 'updown 3 only 1 new'
102507Smax.romanov@nginx.com
103507Smax.romanov@nginx.com        self.get()
1041596Szelenkov@nginx.com        assert self.pids_for_process() == pids_new, 'updown still 3'
105507Smax.romanov@nginx.com
106516Szelenkov@nginx.com        time.sleep(1)
107516Szelenkov@nginx.com
108516Szelenkov@nginx.com        pids = self.pids_for_process()
1091596Szelenkov@nginx.com        assert len(pids) == 2, 'updown stop idle'
110507Smax.romanov@nginx.com
111507Smax.romanov@nginx.com        self.get()
112516Szelenkov@nginx.com        pids_new = self.pids_for_process()
1131596Szelenkov@nginx.com        assert len(pids_new) == 3, 'updown again 3'
1141596Szelenkov@nginx.com        assert pids.issubset(pids_new), 'updown again 3 only 1 new'
115507Smax.romanov@nginx.com
116515Szelenkov@nginx.com        self.stop_all()
117507Smax.romanov@nginx.com
118507Smax.romanov@nginx.com    def test_python_reconfigure(self):
1191416Szelenkov@nginx.com        self.conf_proc({"spare": 2, "max": 6, "idle_timeout": 1})
120507Smax.romanov@nginx.com
121516Szelenkov@nginx.com        pids = self.pids_for_process()
1221596Szelenkov@nginx.com        assert len(pids) == 2, 'reconf 2'
123507Smax.romanov@nginx.com
124507Smax.romanov@nginx.com        self.get()
125516Szelenkov@nginx.com        pids_new = self.pids_for_process()
1261596Szelenkov@nginx.com        assert len(pids_new) == 3, 'reconf 3'
1271596Szelenkov@nginx.com        assert pids.issubset(pids_new), 'reconf 3 only 1 new'
128507Smax.romanov@nginx.com
1291416Szelenkov@nginx.com        self.conf_proc('6', self.app_proc + '/spare')
130515Szelenkov@nginx.com
131516Szelenkov@nginx.com        pids = self.pids_for_process()
1321596Szelenkov@nginx.com        assert len(pids) == 6, 'reconf 6'
133507Smax.romanov@nginx.com
134507Smax.romanov@nginx.com        self.get()
1351596Szelenkov@nginx.com        assert self.pids_for_process() == pids, 'reconf still 6'
136507Smax.romanov@nginx.com
137515Szelenkov@nginx.com        self.stop_all()
138515Szelenkov@nginx.com
139517Szelenkov@nginx.com    def test_python_idle_timeout(self):
1401416Szelenkov@nginx.com        self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
141517Szelenkov@nginx.com
142517Szelenkov@nginx.com        self.get()
143517Szelenkov@nginx.com        pids = self.pids_for_process()
1441596Szelenkov@nginx.com        assert len(pids) == 1, 'idle timeout 1'
145517Szelenkov@nginx.com
146517Szelenkov@nginx.com        time.sleep(1)
147517Szelenkov@nginx.com
148517Szelenkov@nginx.com        self.get()
149517Szelenkov@nginx.com
150517Szelenkov@nginx.com        time.sleep(1)
151517Szelenkov@nginx.com
152517Szelenkov@nginx.com        pids_new = self.pids_for_process()
1531596Szelenkov@nginx.com        assert len(pids_new) == 1, 'idle timeout still 1'
1541596Szelenkov@nginx.com        assert self.pids_for_process() == pids, 'idle timeout still 1 same pid'
155517Szelenkov@nginx.com
156517Szelenkov@nginx.com        time.sleep(1)
157517Szelenkov@nginx.com
1581596Szelenkov@nginx.com        assert len(self.pids_for_process()) == 0, 'idle timed out'
159517Szelenkov@nginx.com
160517Szelenkov@nginx.com    def test_python_processes_connection_keepalive(self):
1611416Szelenkov@nginx.com        self.conf_proc({"spare": 0, "max": 6, "idle_timeout": 2})
162517Szelenkov@nginx.com
1631017Szelenkov@nginx.com        (resp, sock) = self.get(
1641017Szelenkov@nginx.com            headers={'Host': 'localhost', 'Connection': 'keep-alive'},
1651017Szelenkov@nginx.com            start=True,
1661017Szelenkov@nginx.com            read_timeout=1,
1671017Szelenkov@nginx.com        )
1681596Szelenkov@nginx.com        assert len(self.pids_for_process()) == 1, 'keepalive connection 1'
169517Szelenkov@nginx.com
170517Szelenkov@nginx.com        time.sleep(2)
171517Szelenkov@nginx.com
1721596Szelenkov@nginx.com        assert len(self.pids_for_process()) == 0, 'keepalive connection 0'
173517Szelenkov@nginx.com
174517Szelenkov@nginx.com        sock.close()
175517Szelenkov@nginx.com
1761416Szelenkov@nginx.com    def test_python_processes_access(self):
1771416Szelenkov@nginx.com        self.conf_proc('1')
1781416Szelenkov@nginx.com
1791416Szelenkov@nginx.com        path = '/' + self.app_proc
1801596Szelenkov@nginx.com        assert 'error' in self.conf_get(path + '/max')
1811596Szelenkov@nginx.com        assert 'error' in self.conf_get(path + '/spare')
1821596Szelenkov@nginx.com        assert 'error' in self.conf_get(path + '/idle_timeout')
1831416Szelenkov@nginx.com
1841416Szelenkov@nginx.com    def test_python_processes_invalid(self):
1851596Szelenkov@nginx.com        assert 'error' in self.conf(
1861596Szelenkov@nginx.com            {"spare": -1}, self.app_proc
1871596Szelenkov@nginx.com        ), 'negative spare'
1881596Szelenkov@nginx.com        assert 'error' in self.conf({"max": -1}, self.app_proc), 'negative max'
1891596Szelenkov@nginx.com        assert 'error' in self.conf(
1901596Szelenkov@nginx.com            {"idle_timeout": -1}, self.app_proc
1911596Szelenkov@nginx.com        ), 'negative idle_timeout'
1921596Szelenkov@nginx.com        assert 'error' in self.conf(
1931596Szelenkov@nginx.com            {"spare": 2}, self.app_proc
1941596Szelenkov@nginx.com        ), 'spare gt max default'
1951596Szelenkov@nginx.com        assert 'error' in self.conf(
1961596Szelenkov@nginx.com            {"spare": 2, "max": 1}, self.app_proc
1971596Szelenkov@nginx.com        ), 'spare gt max'
1981596Szelenkov@nginx.com        assert 'error' in self.conf(
1991596Szelenkov@nginx.com            {"spare": 0, "max": 0}, self.app_proc
2001596Szelenkov@nginx.com        ), 'max zero'
2011416Szelenkov@nginx.com
202515Szelenkov@nginx.com    def stop_all(self):
2031775Szelenkov@nginx.com        assert 'success' in self.conf({"listeners": {}, "applications": {}})
204507Smax.romanov@nginx.com
2051596Szelenkov@nginx.com        assert len(self.pids_for_process()) == 0, 'stop all'
2061926Smax.romanov@nginx.com
2071926Smax.romanov@nginx.com    def test_python_restart(self, temp_dir):
2081926Smax.romanov@nginx.com        shutil.copyfile(
2091926Smax.romanov@nginx.com            option.test_dir + '/python/restart/v1.py', temp_dir + '/wsgi.py'
2101926Smax.romanov@nginx.com        )
2111926Smax.romanov@nginx.com
2121926Smax.romanov@nginx.com        self.load(
2131926Smax.romanov@nginx.com            temp_dir,
2141926Smax.romanov@nginx.com            name=self.app_name,
2151926Smax.romanov@nginx.com            processes=1,
2161926Smax.romanov@nginx.com            environment={'PYTHONDONTWRITEBYTECODE': '1'},
2171926Smax.romanov@nginx.com        )
2181926Smax.romanov@nginx.com
2191926Smax.romanov@nginx.com        b = self.get()['body']
2201926Smax.romanov@nginx.com        assert b == "v1", 'process started'
2211926Smax.romanov@nginx.com
2221926Smax.romanov@nginx.com        shutil.copyfile(
2231926Smax.romanov@nginx.com            option.test_dir + '/python/restart/v2.py', temp_dir + '/wsgi.py'
2241926Smax.romanov@nginx.com        )
2251926Smax.romanov@nginx.com
2261926Smax.romanov@nginx.com        b = self.get()['body']
2271926Smax.romanov@nginx.com        assert b == "v1", 'still old process'
2281926Smax.romanov@nginx.com
2291926Smax.romanov@nginx.com        assert 'success' in self.conf_get(
2301926Smax.romanov@nginx.com            '/control/applications/' + self.app_name + '/restart'
2311926Smax.romanov@nginx.com        ), 'restart processes'
2321926Smax.romanov@nginx.com
2331926Smax.romanov@nginx.com        b = self.get()['body']
2341926Smax.romanov@nginx.com        assert b == "v2", 'new process started'
2351926Smax.romanov@nginx.com
2361926Smax.romanov@nginx.com        assert 'error' in self.conf_get(
2371926Smax.romanov@nginx.com            '/control/applications/blah/restart'
2381926Smax.romanov@nginx.com        ), 'application incorrect'
2391926Smax.romanov@nginx.com
2401926Smax.romanov@nginx.com        assert 'error' in self.conf_delete(
2411926Smax.romanov@nginx.com            '/control/applications/' + self.app_name + '/restart'
2421926Smax.romanov@nginx.com        ), 'method incorrect'
2431926Smax.romanov@nginx.com
2441926Smax.romanov@nginx.com    def test_python_restart_multi(self):
2451926Smax.romanov@nginx.com        self.conf_proc('2')
2461926Smax.romanov@nginx.com
2471926Smax.romanov@nginx.com        pids = self.pids_for_process()
2481926Smax.romanov@nginx.com        assert len(pids) == 2, 'restart 2 started'
2491926Smax.romanov@nginx.com
2501926Smax.romanov@nginx.com        assert 'success' in self.conf_get(
2511926Smax.romanov@nginx.com            '/control/applications/' + self.app_name + '/restart'
2521926Smax.romanov@nginx.com        ), 'restart processes'
2531926Smax.romanov@nginx.com
2541926Smax.romanov@nginx.com        new_pids = self.pids_for_process()
2551926Smax.romanov@nginx.com        assert len(new_pids) == 2, 'restart still 2'
2561926Smax.romanov@nginx.com
2571926Smax.romanov@nginx.com        assert len(new_pids.intersection(pids)) == 0, 'restart all new'
2581926Smax.romanov@nginx.com
2591926Smax.romanov@nginx.com    def test_python_restart_longstart(self):
2601926Smax.romanov@nginx.com        self.load(
2611926Smax.romanov@nginx.com            'restart',
2621926Smax.romanov@nginx.com            name=self.app_name,
2631926Smax.romanov@nginx.com            module="longstart",
2641926Smax.romanov@nginx.com            processes={"spare": 1, "max": 2, "idle_timeout": 5},
2651926Smax.romanov@nginx.com        )
2661926Smax.romanov@nginx.com
2671926Smax.romanov@nginx.com        assert len(self.pids_for_process()) == 1, 'longstarts == 1'
2681926Smax.romanov@nginx.com
2691971Szelenkov@nginx.com        self.get()
2701971Szelenkov@nginx.com
2711926Smax.romanov@nginx.com        pids = self.pids_for_process()
2721926Smax.romanov@nginx.com        assert len(pids) == 2, 'longstarts == 2'
2731926Smax.romanov@nginx.com
2741926Smax.romanov@nginx.com        assert 'success' in self.conf_get(
2751926Smax.romanov@nginx.com            '/control/applications/' + self.app_name + '/restart'
2761926Smax.romanov@nginx.com        ), 'restart processes'
2771926Smax.romanov@nginx.com
2781926Smax.romanov@nginx.com        # wait for longstarted app
2791926Smax.romanov@nginx.com        time.sleep(2)
2801926Smax.romanov@nginx.com
2811926Smax.romanov@nginx.com        new_pids = self.pids_for_process()
2821926Smax.romanov@nginx.com        assert len(new_pids) == 1, 'restart 1'
2831926Smax.romanov@nginx.com
2841926Smax.romanov@nginx.com        assert len(new_pids.intersection(pids)) == 0, 'restart all new'
285