##// END OF EJS Templates
tests: fixed starting of customized gunicorn with a explicit --bind call
super-admin -
r4976:6c82c462 default
parent child Browse files
Show More
@@ -1,201 +1,208 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import os
22 import os
23 import time
23 import time
24 import tempfile
24 import tempfile
25 import pytest
25 import pytest
26 import subprocess
26 import subprocess
27 import logging
27 import logging
28 from urllib.request import urlopen
28 from urllib.request import urlopen
29 from urllib.error import URLError
29 from urllib.error import URLError
30 import configparser
30 import configparser
31
31
32
32
33 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
33 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS
34 from rhodecode.tests.utils import is_url_reachable
34 from rhodecode.tests.utils import is_url_reachable
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 def get_port(pyramid_config):
39 def get_port(pyramid_config):
40 config = configparser.ConfigParser()
40 config = configparser.ConfigParser()
41 config.read(pyramid_config)
41 config.read(pyramid_config)
42 return config.get('server:main', 'port')
42 return config.get('server:main', 'port')
43
43
44
44
45 def get_host_url(pyramid_config):
45 def get_host_url(pyramid_config):
46 """Construct the host url using the port in the test configuration."""
46 """Construct the host url using the port in the test configuration."""
47 port = get_port(pyramid_config)
47 port = get_port(pyramid_config)
48 return f'127.0.0.1:{port}'
48 return f'127.0.0.1:{port}'
49
49
50
50
51 def assert_no_running_instance(url):
51 def assert_no_running_instance(url):
52 if is_url_reachable(url):
52 if is_url_reachable(url):
53 print(f"Hint: Usually this means another instance of server "
53 print(f"Hint: Usually this means another instance of server "
54 f"is running in the background at {url}.")
54 f"is running in the background at {url}.")
55 pytest.fail(f"Port is not free at {url}, cannot start server at")
55 pytest.fail(f"Port is not free at {url}, cannot start server at")
56
56
57
57
58 class ServerBase(object):
58 class ServerBase(object):
59 _args = []
59 _args = []
60 log_file_name = 'NOT_DEFINED.log'
60 log_file_name = 'NOT_DEFINED.log'
61 status_url_tmpl = 'http://{host}:{port}'
61 status_url_tmpl = 'http://{host}:{port}'
62
62
63 def __init__(self, config_file, log_file):
63 def __init__(self, config_file, log_file):
64 self.config_file = config_file
64 self.config_file = config_file
65 config = configparser.ConfigParser()
65 config = configparser.ConfigParser()
66 config.read(config_file)
66 config.read(config_file)
67
67
68 self._config = {k: v for k, v in config['server:main'].items()}
68 self._config = {k: v for k, v in config['server:main'].items()}
69
69
70 self._args = []
70 self._args = []
71 self.log_file = log_file or os.path.join(
71 self.log_file = log_file or os.path.join(
72 tempfile.gettempdir(), self.log_file_name)
72 tempfile.gettempdir(), self.log_file_name)
73 self.process = None
73 self.process = None
74 self.server_out = None
74 self.server_out = None
75 log.info("Using the {} configuration:{}".format(
75 log.info("Using the {} configuration:{}".format(
76 self.__class__.__name__, config_file))
76 self.__class__.__name__, config_file))
77
77
78 if not os.path.isfile(config_file):
78 if not os.path.isfile(config_file):
79 raise RuntimeError(f'Failed to get config at {config_file}')
79 raise RuntimeError(f'Failed to get config at {config_file}')
80
80
81 @property
81 @property
82 def command(self):
82 def command(self):
83 return ' '.join(self._args)
83 return ' '.join(self._args)
84
84
85 @property
85 @property
86 def bind_addr(self):
87 return '{host}:{port}'.format(**self._config)
88
89 @property
86 def http_url(self):
90 def http_url(self):
87 template = 'http://{host}:{port}/'
91 template = 'http://{host}:{port}/'
88 return template.format(**self._config)
92 return template.format(**self._config)
89
93
90 def host_url(self):
94 def host_url(self):
91 return 'http://' + get_host_url(self.config_file)
95 host = get_host_url(self.config_file)
96 return f'http://{host}'
92
97
93 def get_rc_log(self):
98 def get_rc_log(self):
94 with open(self.log_file) as f:
99 with open(self.log_file) as f:
95 return f.read()
100 return f.read()
96
101
97 def wait_until_ready(self, timeout=30):
102 def wait_until_ready(self, timeout=30):
98 host = self._config['host']
103 host = self._config['host']
99 port = self._config['port']
104 port = self._config['port']
100 status_url = self.status_url_tmpl.format(host=host, port=port)
105 status_url = self.status_url_tmpl.format(host=host, port=port)
101 start = time.time()
106 start = time.time()
102
107
103 while time.time() - start < timeout:
108 while time.time() - start < timeout:
104 try:
109 try:
105 urlopen(status_url)
110 urlopen(status_url)
106 break
111 break
107 except URLError:
112 except URLError:
108 time.sleep(0.2)
113 time.sleep(0.2)
109 else:
114 else:
110 pytest.fail(
115 pytest.fail(
111 "Starting the {} failed or took more than {} "
116 "Starting the {} failed or took more than {} "
112 "seconds. cmd: `{}`".format(
117 "seconds. cmd: `{}`".format(
113 self.__class__.__name__, timeout, self.command))
118 self.__class__.__name__, timeout, self.command))
114
119
115 log.info('Server of {} ready at url {}'.format(
120 log.info('Server of {} ready at url {}'.format(
116 self.__class__.__name__, status_url))
121 self.__class__.__name__, status_url))
117
122
118 def shutdown(self):
123 def shutdown(self):
119 self.process.kill()
124 self.process.kill()
120 self.server_out.flush()
125 self.server_out.flush()
121 self.server_out.close()
126 self.server_out.close()
122
127
123 def get_log_file_with_port(self):
128 def get_log_file_with_port(self):
124 log_file = list(self.log_file.partition('.log'))
129 log_file = list(self.log_file.partition('.log'))
125 log_file.insert(1, get_port(self.config_file))
130 log_file.insert(1, get_port(self.config_file))
126 log_file = ''.join(log_file)
131 log_file = ''.join(log_file)
127 return log_file
132 return log_file
128
133
129
134
130 class RcVCSServer(ServerBase):
135 class RcVCSServer(ServerBase):
131 """
136 """
132 Represents a running VCSServer instance.
137 Represents a running VCSServer instance.
133 """
138 """
134
139
135 log_file_name = 'rc-vcsserver.log'
140 log_file_name = 'rc-vcsserver.log'
136 status_url_tmpl = 'http://{host}:{port}/status'
141 status_url_tmpl = 'http://{host}:{port}/status'
137
142
138 def __init__(self, config_file, log_file=None):
143 def __init__(self, config_file, log_file=None):
139 super(RcVCSServer, self).__init__(config_file, log_file)
144 super(RcVCSServer, self).__init__(config_file, log_file)
140 self._args = ['gunicorn', '--paste', self.config_file]
145 self._args = [
146 'gunicorn', '--bind', self.bind_addr,
147 '--paste', self.config_file]
141
148
142 def start(self):
149 def start(self):
143 env = os.environ.copy()
150 env = os.environ.copy()
144
151
145 self.log_file = self.get_log_file_with_port()
152 self.log_file = self.get_log_file_with_port()
146 self.server_out = open(self.log_file, 'w')
153 self.server_out = open(self.log_file, 'w')
147
154
148 host_url = self.host_url()
155 host_url = self.host_url()
149 assert_no_running_instance(host_url)
156 assert_no_running_instance(host_url)
150
157
151 log.info('rhodecode-vcsserver start command: {}'.format(' '.join(self._args)))
158 print(f'rhodecode-vcsserver starting at: {host_url}')
152 log.info('rhodecode-vcsserver starting at: {}'.format(host_url))
159 print(f'rhodecode-vcsserver command: {self.command}')
153 log.info('rhodecode-vcsserver command: {}'.format(self.command))
160 print(f'rhodecode-vcsserver logfile: {self.log_file}')
154 log.info('rhodecode-vcsserver logfile: {}'.format(self.log_file))
155
161
156 self.process = subprocess.Popen(
162 self.process = subprocess.Popen(
157 self._args, bufsize=0, env=env,
163 self._args, bufsize=0, env=env,
158 stdout=self.server_out, stderr=self.server_out)
164 stdout=self.server_out, stderr=self.server_out)
159
165
160
166
161 class RcWebServer(ServerBase):
167 class RcWebServer(ServerBase):
162 """
168 """
163 Represents a running RCE web server used as a test fixture.
169 Represents a running RCE web server used as a test fixture.
164 """
170 """
165
171
166 log_file_name = 'rc-web.log'
172 log_file_name = 'rc-web.log'
167 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
173 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
168
174
169 def __init__(self, config_file, log_file=None):
175 def __init__(self, config_file, log_file=None):
170 super(RcWebServer, self).__init__(config_file, log_file)
176 super(RcWebServer, self).__init__(config_file, log_file)
171 self._args = [
177 self._args = [
172 'gunicorn', '--worker-class', 'gevent', '--paste', config_file]
178 'gunicorn', '--bind', self.bind_addr, '--worker-class', 'gevent',
179 '--paste', self.config_file]
173
180
174 def start(self):
181 def start(self):
175 env = os.environ.copy()
182 env = os.environ.copy()
176 env['RC_NO_TMP_PATH'] = '1'
183 env['RC_NO_TMP_PATH'] = '1'
177
184
178 self.log_file = self.get_log_file_with_port()
185 self.log_file = self.get_log_file_with_port()
179 self.server_out = open(self.log_file, 'w')
186 self.server_out = open(self.log_file, 'w')
180
187
181 host_url = self.host_url()
188 host_url = self.host_url()
182 assert_no_running_instance(host_url)
189 assert_no_running_instance(host_url)
183
190
184 log.info('rhodecode-web starting at: {}'.format(host_url))
191 print(f'rhodecode-web starting at: {host_url}')
185 log.info('rhodecode-web command: {}'.format(self.command))
192 print(f'rhodecode-web command: {self.command}')
186 log.info('rhodecode-web logfile: {}'.format(self.log_file))
193 print(f'rhodecode-web logfile: {self.log_file}')
187
194
188 self.process = subprocess.Popen(
195 self.process = subprocess.Popen(
189 self._args, bufsize=0, env=env,
196 self._args, bufsize=0, env=env,
190 stdout=self.server_out, stderr=self.server_out)
197 stdout=self.server_out, stderr=self.server_out)
191
198
192 def repo_clone_url(self, repo_name, **kwargs):
199 def repo_clone_url(self, repo_name, **kwargs):
193 params = {
200 params = {
194 'user': TEST_USER_ADMIN_LOGIN,
201 'user': TEST_USER_ADMIN_LOGIN,
195 'passwd': TEST_USER_ADMIN_PASS,
202 'passwd': TEST_USER_ADMIN_PASS,
196 'host': get_host_url(self.config_file),
203 'host': get_host_url(self.config_file),
197 'cloned_repo': repo_name,
204 'cloned_repo': repo_name,
198 }
205 }
199 params.update(**kwargs)
206 params.update(**kwargs)
200 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
207 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
201 return _url
208 return _url
General Comments 0
You need to be logged in to leave comments. Login now