##// END OF EJS Templates
server-utils: report server ready state
marcink -
r2466:f1a30267 default
parent child Browse files
Show More
@@ -1,194 +1,197 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 subprocess32
26 import subprocess32
27 import configobj
27 import configobj
28
28
29 from urllib2 import urlopen, URLError
29 from urllib2 import urlopen, URLError
30 from pyramid.compat import configparser
30 from pyramid.compat 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
36
37 def get_port(pyramid_config):
37 def get_port(pyramid_config):
38 config = configparser.ConfigParser()
38 config = configparser.ConfigParser()
39 config.read(pyramid_config)
39 config.read(pyramid_config)
40 return config.get('server:main', 'port')
40 return config.get('server:main', 'port')
41
41
42
42
43 def get_host_url(pyramid_config):
43 def get_host_url(pyramid_config):
44 """Construct the host url using the port in the test configuration."""
44 """Construct the host url using the port in the test configuration."""
45 return '127.0.0.1:%s' % get_port(pyramid_config)
45 return '127.0.0.1:%s' % get_port(pyramid_config)
46
46
47
47
48 def assert_no_running_instance(url):
48 def assert_no_running_instance(url):
49 if is_url_reachable(url):
49 if is_url_reachable(url):
50 print("Hint: Usually this means another instance of server "
50 print("Hint: Usually this means another instance of server "
51 "is running in the background at %s." % url)
51 "is running in the background at %s." % url)
52 pytest.fail(
52 pytest.fail(
53 "Port is not free at %s, cannot start server at" % url)
53 "Port is not free at %s, cannot start server at" % url)
54
54
55
55
56 class ServerBase(object):
56 class ServerBase(object):
57 _args = []
57 _args = []
58 log_file_name = 'NOT_DEFINED.log'
58 log_file_name = 'NOT_DEFINED.log'
59 status_url_tmpl = 'http://{host}:{port}'
59 status_url_tmpl = 'http://{host}:{port}'
60
60
61 def __init__(self, config_file, log_file):
61 def __init__(self, config_file, log_file):
62 self.config_file = config_file
62 self.config_file = config_file
63 config_data = configobj.ConfigObj(config_file)
63 config_data = configobj.ConfigObj(config_file)
64 self._config = config_data['server:main']
64 self._config = config_data['server:main']
65
65
66 self._args = []
66 self._args = []
67 self.log_file = log_file or os.path.join(
67 self.log_file = log_file or os.path.join(
68 tempfile.gettempdir(), self.log_file_name)
68 tempfile.gettempdir(), self.log_file_name)
69 self.process = None
69 self.process = None
70 self.server_out = None
70 self.server_out = None
71 print("Using the {} configuration:{}".format(
71 print("Using the {} configuration:{}".format(
72 self.__class__.__name__, config_file))
72 self.__class__.__name__, config_file))
73
73
74 if not os.path.isfile(config_file):
74 if not os.path.isfile(config_file):
75 raise RuntimeError('Failed to get config at {}'.format(config_file))
75 raise RuntimeError('Failed to get config at {}'.format(config_file))
76
76
77 @property
77 @property
78 def command(self):
78 def command(self):
79 return ' '.join(self._args)
79 return ' '.join(self._args)
80
80
81 @property
81 @property
82 def http_url(self):
82 def http_url(self):
83 template = 'http://{host}:{port}/'
83 template = 'http://{host}:{port}/'
84 return template.format(**self._config)
84 return template.format(**self._config)
85
85
86 def host_url(self):
86 def host_url(self):
87 return 'http://' + get_host_url(self.config_file)
87 return 'http://' + get_host_url(self.config_file)
88
88
89 def get_rc_log(self):
89 def get_rc_log(self):
90 with open(self.log_file) as f:
90 with open(self.log_file) as f:
91 return f.read()
91 return f.read()
92
92
93 def wait_until_ready(self, timeout=30):
93 def wait_until_ready(self, timeout=30):
94 host = self._config['host']
94 host = self._config['host']
95 port = self._config['port']
95 port = self._config['port']
96 status_url = self.status_url_tmpl.format(host=host, port=port)
96 status_url = self.status_url_tmpl.format(host=host, port=port)
97 start = time.time()
97 start = time.time()
98
98
99 while time.time() - start < timeout:
99 while time.time() - start < timeout:
100 try:
100 try:
101 urlopen(status_url)
101 urlopen(status_url)
102 break
102 break
103 except URLError:
103 except URLError:
104 time.sleep(0.2)
104 time.sleep(0.2)
105 else:
105 else:
106 pytest.fail(
106 pytest.fail(
107 "Starting the {} failed or took more than {} "
107 "Starting the {} failed or took more than {} "
108 "seconds. cmd: `{}`".format(
108 "seconds. cmd: `{}`".format(
109 self.__class__.__name__, timeout, self.command))
109 self.__class__.__name__, timeout, self.command))
110
110
111 print('Server of {} ready at url {}'.format(
112 self.__class__.__name__, status_url))
113
111 def shutdown(self):
114 def shutdown(self):
112 self.process.kill()
115 self.process.kill()
113 self.server_out.flush()
116 self.server_out.flush()
114 self.server_out.close()
117 self.server_out.close()
115
118
116 def get_log_file_with_port(self):
119 def get_log_file_with_port(self):
117 log_file = list(self.log_file.partition('.log'))
120 log_file = list(self.log_file.partition('.log'))
118 log_file.insert(1, get_port(self.config_file))
121 log_file.insert(1, get_port(self.config_file))
119 log_file = ''.join(log_file)
122 log_file = ''.join(log_file)
120 return log_file
123 return log_file
121
124
122
125
123 class RcVCSServer(ServerBase):
126 class RcVCSServer(ServerBase):
124 """
127 """
125 Represents a running VCSServer instance.
128 Represents a running VCSServer instance.
126 """
129 """
127
130
128 log_file_name = 'rc-vcsserver.log'
131 log_file_name = 'rc-vcsserver.log'
129 status_url_tmpl = 'http://{host}:{port}/status'
132 status_url_tmpl = 'http://{host}:{port}/status'
130
133
131 def __init__(self, config_file, log_file=None):
134 def __init__(self, config_file, log_file=None):
132 super(RcVCSServer, self).__init__(config_file, log_file)
135 super(RcVCSServer, self).__init__(config_file, log_file)
133 self._args = [
136 self._args = [
134 'gunicorn', '--paste', self.config_file]
137 'gunicorn', '--paste', self.config_file]
135
138
136 def start(self):
139 def start(self):
137 env = os.environ.copy()
140 env = os.environ.copy()
138
141
139 self.log_file = self.get_log_file_with_port()
142 self.log_file = self.get_log_file_with_port()
140 self.server_out = open(self.log_file, 'w')
143 self.server_out = open(self.log_file, 'w')
141
144
142 host_url = self.host_url()
145 host_url = self.host_url()
143 assert_no_running_instance(host_url)
146 assert_no_running_instance(host_url)
144
147
145 print('rhodecode-vcsserver starting at: {}'.format(host_url))
148 print('rhodecode-vcsserver starting at: {}'.format(host_url))
146 print('rhodecode-vcsserver command: {}'.format(self.command))
149 print('rhodecode-vcsserver command: {}'.format(self.command))
147 print('rhodecode-vcsserver logfile: {}'.format(self.log_file))
150 print('rhodecode-vcsserver logfile: {}'.format(self.log_file))
148
151
149 self.process = subprocess32.Popen(
152 self.process = subprocess32.Popen(
150 self._args, bufsize=0, env=env,
153 self._args, bufsize=0, env=env,
151 stdout=self.server_out, stderr=self.server_out)
154 stdout=self.server_out, stderr=self.server_out)
152
155
153
156
154 class RcWebServer(ServerBase):
157 class RcWebServer(ServerBase):
155 """
158 """
156 Represents a running RCE web server used as a test fixture.
159 Represents a running RCE web server used as a test fixture.
157 """
160 """
158
161
159 log_file_name = 'rc-web.log'
162 log_file_name = 'rc-web.log'
160 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
163 status_url_tmpl = 'http://{host}:{port}/_admin/ops/ping'
161
164
162 def __init__(self, config_file, log_file=None):
165 def __init__(self, config_file, log_file=None):
163 super(RcWebServer, self).__init__(config_file, log_file)
166 super(RcWebServer, self).__init__(config_file, log_file)
164 self._args = [
167 self._args = [
165 'gunicorn', '--worker-class', 'gevent', '--paste', config_file]
168 'gunicorn', '--worker-class', 'gevent', '--paste', config_file]
166
169
167 def start(self):
170 def start(self):
168 env = os.environ.copy()
171 env = os.environ.copy()
169 env['RC_NO_TMP_PATH'] = '1'
172 env['RC_NO_TMP_PATH'] = '1'
170
173
171 self.log_file = self.get_log_file_with_port()
174 self.log_file = self.get_log_file_with_port()
172 self.server_out = open(self.log_file, 'w')
175 self.server_out = open(self.log_file, 'w')
173
176
174 host_url = self.host_url()
177 host_url = self.host_url()
175 assert_no_running_instance(host_url)
178 assert_no_running_instance(host_url)
176
179
177 print('rhodecode-web starting at: {}'.format(host_url))
180 print('rhodecode-web starting at: {}'.format(host_url))
178 print('rhodecode-web command: {}'.format(self.command))
181 print('rhodecode-web command: {}'.format(self.command))
179 print('rhodecode-web logfile: {}'.format(self.log_file))
182 print('rhodecode-web logfile: {}'.format(self.log_file))
180
183
181 self.process = subprocess32.Popen(
184 self.process = subprocess32.Popen(
182 self._args, bufsize=0, env=env,
185 self._args, bufsize=0, env=env,
183 stdout=self.server_out, stderr=self.server_out)
186 stdout=self.server_out, stderr=self.server_out)
184
187
185 def repo_clone_url(self, repo_name, **kwargs):
188 def repo_clone_url(self, repo_name, **kwargs):
186 params = {
189 params = {
187 'user': TEST_USER_ADMIN_LOGIN,
190 'user': TEST_USER_ADMIN_LOGIN,
188 'passwd': TEST_USER_ADMIN_PASS,
191 'passwd': TEST_USER_ADMIN_PASS,
189 'host': get_host_url(self.config_file),
192 'host': get_host_url(self.config_file),
190 'cloned_repo': repo_name,
193 'cloned_repo': repo_name,
191 }
194 }
192 params.update(**kwargs)
195 params.update(**kwargs)
193 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
196 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
194 return _url
197 return _url
General Comments 0
You need to be logged in to leave comments. Login now