##// END OF EJS Templates
git-lfs: create default LFS storage on new installation...
marcink -
r1564:41524466 default
parent child Browse files
Show More
@@ -1,84 +1,87 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 import os
21 import os
22 import shlex
22 import shlex
23 import platform
23 import platform
24
24
25 from rhodecode.model import init_model
25 from rhodecode.model import init_model
26
26
27
27
28
28
29 def configure_vcs(config):
29 def configure_vcs(config):
30 """
30 """
31 Patch VCS config with some RhodeCode specific stuff
31 Patch VCS config with some RhodeCode specific stuff
32 """
32 """
33 from rhodecode.lib.vcs import conf
33 from rhodecode.lib.vcs import conf
34 conf.settings.BACKENDS = {
34 conf.settings.BACKENDS = {
35 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
35 'hg': 'rhodecode.lib.vcs.backends.hg.MercurialRepository',
36 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
36 'git': 'rhodecode.lib.vcs.backends.git.GitRepository',
37 'svn': 'rhodecode.lib.vcs.backends.svn.SubversionRepository',
37 'svn': 'rhodecode.lib.vcs.backends.svn.SubversionRepository',
38 }
38 }
39
39
40 conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol']
40 conf.settings.HOOKS_PROTOCOL = config['vcs.hooks.protocol']
41 conf.settings.HOOKS_DIRECT_CALLS = config['vcs.hooks.direct_calls']
41 conf.settings.HOOKS_DIRECT_CALLS = config['vcs.hooks.direct_calls']
42 conf.settings.GIT_REV_FILTER = shlex.split(config['git_rev_filter'])
42 conf.settings.GIT_REV_FILTER = shlex.split(config['git_rev_filter'])
43 conf.settings.DEFAULT_ENCODINGS = config['default_encoding']
43 conf.settings.DEFAULT_ENCODINGS = config['default_encoding']
44 conf.settings.ALIASES[:] = config['vcs.backends']
44 conf.settings.ALIASES[:] = config['vcs.backends']
45 conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version']
45 conf.settings.SVN_COMPATIBLE_VERSION = config['vcs.svn.compatible_version']
46
46
47
47
48 def initialize_database(config):
48 def initialize_database(config):
49 from rhodecode.lib.utils2 import engine_from_config, get_encryption_key
49 from rhodecode.lib.utils2 import engine_from_config, get_encryption_key
50 engine = engine_from_config(config, 'sqlalchemy.db1.')
50 engine = engine_from_config(config, 'sqlalchemy.db1.')
51 init_model(engine, encryption_key=get_encryption_key(config))
51 init_model(engine, encryption_key=get_encryption_key(config))
52
52
53
53
54 def initialize_test_environment(settings, test_env=None):
54 def initialize_test_environment(settings, test_env=None):
55 if test_env is None:
55 if test_env is None:
56 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
56 test_env = not int(os.environ.get('RC_NO_TMP_PATH', 0))
57
57
58 from rhodecode.lib.utils import (
58 from rhodecode.lib.utils import (
59 create_test_directory, create_test_database, create_test_repositories,
59 create_test_directory, create_test_database, create_test_repositories,
60 create_test_index)
60 create_test_index)
61 from rhodecode.tests import TESTS_TMP_PATH
61 from rhodecode.tests import TESTS_TMP_PATH
62 from rhodecode.lib.vcs.backends.hg import largefiles_store
62 from rhodecode.lib.vcs.backends.hg import largefiles_store
63 from rhodecode.lib.vcs.backends.git import lfs_store
64
63 # test repos
65 # test repos
64 if test_env:
66 if test_env:
65 create_test_directory(TESTS_TMP_PATH)
67 create_test_directory(TESTS_TMP_PATH)
66 # large object stores
68 # large object stores
67 create_test_directory(largefiles_store(TESTS_TMP_PATH))
69 create_test_directory(largefiles_store(TESTS_TMP_PATH))
70 create_test_directory(lfs_store(TESTS_TMP_PATH))
68
71
69 create_test_database(TESTS_TMP_PATH, settings)
72 create_test_database(TESTS_TMP_PATH, settings)
70 create_test_repositories(TESTS_TMP_PATH, settings)
73 create_test_repositories(TESTS_TMP_PATH, settings)
71 create_test_index(TESTS_TMP_PATH, settings)
74 create_test_index(TESTS_TMP_PATH, settings)
72
75
73
76
74 def get_vcs_server_protocol(config):
77 def get_vcs_server_protocol(config):
75 return config['vcs.server.protocol']
78 return config['vcs.server.protocol']
76
79
77
80
78 def set_instance_id(config):
81 def set_instance_id(config):
79 """ Sets a dynamic generated config['instance_id'] if missing or '*' """
82 """ Sets a dynamic generated config['instance_id'] if missing or '*' """
80
83
81 config['instance_id'] = config.get('instance_id') or ''
84 config['instance_id'] = config.get('instance_id') or ''
82 if config['instance_id'] == '*' or not config['instance_id']:
85 if config['instance_id'] == '*' or not config['instance_id']:
83 _platform_id = platform.uname()[1] or 'instance'
86 _platform_id = platform.uname()[1] or 'instance'
84 config['instance_id'] = '%s-%s' % (_platform_id, os.getpid())
87 config['instance_id'] = '%s-%s' % (_platform_id, os.getpid())
@@ -1,601 +1,611 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 Database creation, and setup module for RhodeCode Enterprise. Used for creation
22 Database creation, and setup module for RhodeCode Enterprise. Used for creation
23 of database as well as for migration operations
23 of database as well as for migration operations
24 """
24 """
25
25
26 import os
26 import os
27 import sys
27 import sys
28 import time
28 import time
29 import uuid
29 import uuid
30 import logging
30 import logging
31 import getpass
31 import getpass
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from sqlalchemy.engine import create_engine
34 from sqlalchemy.engine import create_engine
35
35
36 from rhodecode import __dbversion__
36 from rhodecode import __dbversion__
37 from rhodecode.model import init_model
37 from rhodecode.model import init_model
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.db import (
39 from rhodecode.model.db import (
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
40 User, Permission, RhodeCodeUi, RhodeCodeSetting, UserToPerm,
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
41 DbMigrateVersion, RepoGroup, UserRepoGroupToPerm, CacheKey, Repository)
42 from rhodecode.model.meta import Session, Base
42 from rhodecode.model.meta import Session, Base
43 from rhodecode.model.permission import PermissionModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.repo_group import RepoGroupModel
45 from rhodecode.model.repo_group import RepoGroupModel
46 from rhodecode.model.settings import SettingsModel
46 from rhodecode.model.settings import SettingsModel
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 def notify(msg):
52 def notify(msg):
53 """
53 """
54 Notification for migrations messages
54 Notification for migrations messages
55 """
55 """
56 ml = len(msg) + (4 * 2)
56 ml = len(msg) + (4 * 2)
57 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
57 print('\n%s\n*** %s ***\n%s' % ('*' * ml, msg, '*' * ml)).upper()
58
58
59
59
60 class DbManage(object):
60 class DbManage(object):
61
61
62 def __init__(self, log_sql, dbconf, root, tests=False,
62 def __init__(self, log_sql, dbconf, root, tests=False,
63 SESSION=None, cli_args={}):
63 SESSION=None, cli_args={}):
64 self.dbname = dbconf.split('/')[-1]
64 self.dbname = dbconf.split('/')[-1]
65 self.tests = tests
65 self.tests = tests
66 self.root = root
66 self.root = root
67 self.dburi = dbconf
67 self.dburi = dbconf
68 self.log_sql = log_sql
68 self.log_sql = log_sql
69 self.db_exists = False
69 self.db_exists = False
70 self.cli_args = cli_args
70 self.cli_args = cli_args
71 self.init_db(SESSION=SESSION)
71 self.init_db(SESSION=SESSION)
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
72 self.ask_ok = self.get_ask_ok_func(self.cli_args.get('force_ask'))
73
73
74 def get_ask_ok_func(self, param):
74 def get_ask_ok_func(self, param):
75 if param not in [None]:
75 if param not in [None]:
76 # return a function lambda that has a default set to param
76 # return a function lambda that has a default set to param
77 return lambda *args, **kwargs: param
77 return lambda *args, **kwargs: param
78 else:
78 else:
79 from rhodecode.lib.utils import ask_ok
79 from rhodecode.lib.utils import ask_ok
80 return ask_ok
80 return ask_ok
81
81
82 def init_db(self, SESSION=None):
82 def init_db(self, SESSION=None):
83 if SESSION:
83 if SESSION:
84 self.sa = SESSION
84 self.sa = SESSION
85 else:
85 else:
86 # init new sessions
86 # init new sessions
87 engine = create_engine(self.dburi, echo=self.log_sql)
87 engine = create_engine(self.dburi, echo=self.log_sql)
88 init_model(engine)
88 init_model(engine)
89 self.sa = Session()
89 self.sa = Session()
90
90
91 def create_tables(self, override=False):
91 def create_tables(self, override=False):
92 """
92 """
93 Create a auth database
93 Create a auth database
94 """
94 """
95
95
96 log.info("Existing database with the same name is going to be destroyed.")
96 log.info("Existing database with the same name is going to be destroyed.")
97 log.info("Setup command will run DROP ALL command on that database.")
97 log.info("Setup command will run DROP ALL command on that database.")
98 if self.tests:
98 if self.tests:
99 destroy = True
99 destroy = True
100 else:
100 else:
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
101 destroy = self.ask_ok('Are you sure that you want to destroy the old database? [y/n]')
102 if not destroy:
102 if not destroy:
103 log.info('Nothing done.')
103 log.info('Nothing done.')
104 sys.exit(0)
104 sys.exit(0)
105 if destroy:
105 if destroy:
106 Base.metadata.drop_all()
106 Base.metadata.drop_all()
107
107
108 checkfirst = not override
108 checkfirst = not override
109 Base.metadata.create_all(checkfirst=checkfirst)
109 Base.metadata.create_all(checkfirst=checkfirst)
110 log.info('Created tables for %s' % self.dbname)
110 log.info('Created tables for %s' % self.dbname)
111
111
112 def set_db_version(self):
112 def set_db_version(self):
113 ver = DbMigrateVersion()
113 ver = DbMigrateVersion()
114 ver.version = __dbversion__
114 ver.version = __dbversion__
115 ver.repository_id = 'rhodecode_db_migrations'
115 ver.repository_id = 'rhodecode_db_migrations'
116 ver.repository_path = 'versions'
116 ver.repository_path = 'versions'
117 self.sa.add(ver)
117 self.sa.add(ver)
118 log.info('db version set to: %s' % __dbversion__)
118 log.info('db version set to: %s' % __dbversion__)
119
119
120 def run_pre_migration_tasks(self):
120 def run_pre_migration_tasks(self):
121 """
121 """
122 Run various tasks before actually doing migrations
122 Run various tasks before actually doing migrations
123 """
123 """
124 # delete cache keys on each upgrade
124 # delete cache keys on each upgrade
125 total = CacheKey.query().count()
125 total = CacheKey.query().count()
126 log.info("Deleting (%s) cache keys now...", total)
126 log.info("Deleting (%s) cache keys now...", total)
127 CacheKey.delete_all_cache()
127 CacheKey.delete_all_cache()
128
128
129 def upgrade(self):
129 def upgrade(self):
130 """
130 """
131 Upgrades given database schema to given revision following
131 Upgrades given database schema to given revision following
132 all needed steps, to perform the upgrade
132 all needed steps, to perform the upgrade
133
133
134 """
134 """
135
135
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
136 from rhodecode.lib.dbmigrate.migrate.versioning import api
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
137 from rhodecode.lib.dbmigrate.migrate.exceptions import \
138 DatabaseNotControlledError
138 DatabaseNotControlledError
139
139
140 if 'sqlite' in self.dburi:
140 if 'sqlite' in self.dburi:
141 print (
141 print (
142 '********************** WARNING **********************\n'
142 '********************** WARNING **********************\n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
143 'Make sure your version of sqlite is at least 3.7.X. \n'
144 'Earlier versions are known to fail on some migrations\n'
144 'Earlier versions are known to fail on some migrations\n'
145 '*****************************************************\n')
145 '*****************************************************\n')
146
146
147 upgrade = self.ask_ok(
147 upgrade = self.ask_ok(
148 'You are about to perform a database upgrade. Make '
148 'You are about to perform a database upgrade. Make '
149 'sure you have backed up your database. '
149 'sure you have backed up your database. '
150 'Continue ? [y/n]')
150 'Continue ? [y/n]')
151 if not upgrade:
151 if not upgrade:
152 log.info('No upgrade performed')
152 log.info('No upgrade performed')
153 sys.exit(0)
153 sys.exit(0)
154
154
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
155 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
156 'rhodecode/lib/dbmigrate')
156 'rhodecode/lib/dbmigrate')
157 db_uri = self.dburi
157 db_uri = self.dburi
158
158
159 try:
159 try:
160 curr_version = api.db_version(db_uri, repository_path)
160 curr_version = api.db_version(db_uri, repository_path)
161 msg = ('Found current database under version '
161 msg = ('Found current database under version '
162 'control with version %s' % curr_version)
162 'control with version %s' % curr_version)
163
163
164 except (RuntimeError, DatabaseNotControlledError):
164 except (RuntimeError, DatabaseNotControlledError):
165 curr_version = 1
165 curr_version = 1
166 msg = ('Current database is not under version control. Setting '
166 msg = ('Current database is not under version control. Setting '
167 'as version %s' % curr_version)
167 'as version %s' % curr_version)
168 api.version_control(db_uri, repository_path, curr_version)
168 api.version_control(db_uri, repository_path, curr_version)
169
169
170 notify(msg)
170 notify(msg)
171
171
172 self.run_pre_migration_tasks()
172 self.run_pre_migration_tasks()
173
173
174 if curr_version == __dbversion__:
174 if curr_version == __dbversion__:
175 log.info('This database is already at the newest version')
175 log.info('This database is already at the newest version')
176 sys.exit(0)
176 sys.exit(0)
177
177
178 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
178 upgrade_steps = range(curr_version + 1, __dbversion__ + 1)
179 notify('attempting to upgrade database from '
179 notify('attempting to upgrade database from '
180 'version %s to version %s' % (curr_version, __dbversion__))
180 'version %s to version %s' % (curr_version, __dbversion__))
181
181
182 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
182 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
183 _step = None
183 _step = None
184 for step in upgrade_steps:
184 for step in upgrade_steps:
185 notify('performing upgrade step %s' % step)
185 notify('performing upgrade step %s' % step)
186 time.sleep(0.5)
186 time.sleep(0.5)
187
187
188 api.upgrade(db_uri, repository_path, step)
188 api.upgrade(db_uri, repository_path, step)
189 self.sa.rollback()
189 self.sa.rollback()
190 notify('schema upgrade for step %s completed' % (step,))
190 notify('schema upgrade for step %s completed' % (step,))
191
191
192 _step = step
192 _step = step
193
193
194 notify('upgrade to version %s successful' % _step)
194 notify('upgrade to version %s successful' % _step)
195
195
196 def fix_repo_paths(self):
196 def fix_repo_paths(self):
197 """
197 """
198 Fixes an old RhodeCode version path into new one without a '*'
198 Fixes an old RhodeCode version path into new one without a '*'
199 """
199 """
200
200
201 paths = self.sa.query(RhodeCodeUi)\
201 paths = self.sa.query(RhodeCodeUi)\
202 .filter(RhodeCodeUi.ui_key == '/')\
202 .filter(RhodeCodeUi.ui_key == '/')\
203 .scalar()
203 .scalar()
204
204
205 paths.ui_value = paths.ui_value.replace('*', '')
205 paths.ui_value = paths.ui_value.replace('*', '')
206
206
207 try:
207 try:
208 self.sa.add(paths)
208 self.sa.add(paths)
209 self.sa.commit()
209 self.sa.commit()
210 except Exception:
210 except Exception:
211 self.sa.rollback()
211 self.sa.rollback()
212 raise
212 raise
213
213
214 def fix_default_user(self):
214 def fix_default_user(self):
215 """
215 """
216 Fixes an old default user with some 'nicer' default values,
216 Fixes an old default user with some 'nicer' default values,
217 used mostly for anonymous access
217 used mostly for anonymous access
218 """
218 """
219 def_user = self.sa.query(User)\
219 def_user = self.sa.query(User)\
220 .filter(User.username == User.DEFAULT_USER)\
220 .filter(User.username == User.DEFAULT_USER)\
221 .one()
221 .one()
222
222
223 def_user.name = 'Anonymous'
223 def_user.name = 'Anonymous'
224 def_user.lastname = 'User'
224 def_user.lastname = 'User'
225 def_user.email = User.DEFAULT_USER_EMAIL
225 def_user.email = User.DEFAULT_USER_EMAIL
226
226
227 try:
227 try:
228 self.sa.add(def_user)
228 self.sa.add(def_user)
229 self.sa.commit()
229 self.sa.commit()
230 except Exception:
230 except Exception:
231 self.sa.rollback()
231 self.sa.rollback()
232 raise
232 raise
233
233
234 def fix_settings(self):
234 def fix_settings(self):
235 """
235 """
236 Fixes rhodecode settings and adds ga_code key for google analytics
236 Fixes rhodecode settings and adds ga_code key for google analytics
237 """
237 """
238
238
239 hgsettings3 = RhodeCodeSetting('ga_code', '')
239 hgsettings3 = RhodeCodeSetting('ga_code', '')
240
240
241 try:
241 try:
242 self.sa.add(hgsettings3)
242 self.sa.add(hgsettings3)
243 self.sa.commit()
243 self.sa.commit()
244 except Exception:
244 except Exception:
245 self.sa.rollback()
245 self.sa.rollback()
246 raise
246 raise
247
247
248 def create_admin_and_prompt(self):
248 def create_admin_and_prompt(self):
249
249
250 # defaults
250 # defaults
251 defaults = self.cli_args
251 defaults = self.cli_args
252 username = defaults.get('username')
252 username = defaults.get('username')
253 password = defaults.get('password')
253 password = defaults.get('password')
254 email = defaults.get('email')
254 email = defaults.get('email')
255
255
256 if username is None:
256 if username is None:
257 username = raw_input('Specify admin username:')
257 username = raw_input('Specify admin username:')
258 if password is None:
258 if password is None:
259 password = self._get_admin_password()
259 password = self._get_admin_password()
260 if not password:
260 if not password:
261 # second try
261 # second try
262 password = self._get_admin_password()
262 password = self._get_admin_password()
263 if not password:
263 if not password:
264 sys.exit()
264 sys.exit()
265 if email is None:
265 if email is None:
266 email = raw_input('Specify admin email:')
266 email = raw_input('Specify admin email:')
267 api_key = self.cli_args.get('api_key')
267 api_key = self.cli_args.get('api_key')
268 self.create_user(username, password, email, True,
268 self.create_user(username, password, email, True,
269 strict_creation_check=False,
269 strict_creation_check=False,
270 api_key=api_key)
270 api_key=api_key)
271
271
272 def _get_admin_password(self):
272 def _get_admin_password(self):
273 password = getpass.getpass('Specify admin password '
273 password = getpass.getpass('Specify admin password '
274 '(min 6 chars):')
274 '(min 6 chars):')
275 confirm = getpass.getpass('Confirm password:')
275 confirm = getpass.getpass('Confirm password:')
276
276
277 if password != confirm:
277 if password != confirm:
278 log.error('passwords mismatch')
278 log.error('passwords mismatch')
279 return False
279 return False
280 if len(password) < 6:
280 if len(password) < 6:
281 log.error('password is too short - use at least 6 characters')
281 log.error('password is too short - use at least 6 characters')
282 return False
282 return False
283
283
284 return password
284 return password
285
285
286 def create_test_admin_and_users(self):
286 def create_test_admin_and_users(self):
287 log.info('creating admin and regular test users')
287 log.info('creating admin and regular test users')
288 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
288 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, \
289 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
289 TEST_USER_ADMIN_PASS, TEST_USER_ADMIN_EMAIL, \
290 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
290 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, \
291 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
291 TEST_USER_REGULAR_EMAIL, TEST_USER_REGULAR2_LOGIN, \
292 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
292 TEST_USER_REGULAR2_PASS, TEST_USER_REGULAR2_EMAIL
293
293
294 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
294 self.create_user(TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,
295 TEST_USER_ADMIN_EMAIL, True, api_key=True)
295 TEST_USER_ADMIN_EMAIL, True, api_key=True)
296
296
297 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
297 self.create_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS,
298 TEST_USER_REGULAR_EMAIL, False, api_key=True)
298 TEST_USER_REGULAR_EMAIL, False, api_key=True)
299
299
300 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
300 self.create_user(TEST_USER_REGULAR2_LOGIN, TEST_USER_REGULAR2_PASS,
301 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
301 TEST_USER_REGULAR2_EMAIL, False, api_key=True)
302
302
303 def create_ui_settings(self, repo_store_path):
303 def create_ui_settings(self, repo_store_path):
304 """
304 """
305 Creates ui settings, fills out hooks
305 Creates ui settings, fills out hooks
306 and disables dotencode
306 and disables dotencode
307 """
307 """
308 settings_model = SettingsModel(sa=self.sa)
308 settings_model = SettingsModel(sa=self.sa)
309 from rhodecode.lib.vcs.backends.hg import largefiles_store
309 from rhodecode.lib.vcs.backends.hg import largefiles_store
310 from rhodecode.lib.vcs.backends.git import lfs_store
310
311
311 # Build HOOKS
312 # Build HOOKS
312 hooks = [
313 hooks = [
313 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
314 (RhodeCodeUi.HOOK_REPO_SIZE, 'python:vcsserver.hooks.repo_size'),
314
315
315 # HG
316 # HG
316 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
317 (RhodeCodeUi.HOOK_PRE_PULL, 'python:vcsserver.hooks.pre_pull'),
317 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
318 (RhodeCodeUi.HOOK_PULL, 'python:vcsserver.hooks.log_pull_action'),
318 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
319 (RhodeCodeUi.HOOK_PRE_PUSH, 'python:vcsserver.hooks.pre_push'),
319 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
320 (RhodeCodeUi.HOOK_PRETX_PUSH, 'python:vcsserver.hooks.pre_push'),
320 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
321 (RhodeCodeUi.HOOK_PUSH, 'python:vcsserver.hooks.log_push_action'),
321
322
322 ]
323 ]
323
324
324 for key, value in hooks:
325 for key, value in hooks:
325 hook_obj = settings_model.get_ui_by_key(key)
326 hook_obj = settings_model.get_ui_by_key(key)
326 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
327 hooks2 = hook_obj if hook_obj else RhodeCodeUi()
327 hooks2.ui_section = 'hooks'
328 hooks2.ui_section = 'hooks'
328 hooks2.ui_key = key
329 hooks2.ui_key = key
329 hooks2.ui_value = value
330 hooks2.ui_value = value
330 self.sa.add(hooks2)
331 self.sa.add(hooks2)
331
332
332 # enable largefiles
333 # enable largefiles
333 largefiles = RhodeCodeUi()
334 largefiles = RhodeCodeUi()
334 largefiles.ui_section = 'extensions'
335 largefiles.ui_section = 'extensions'
335 largefiles.ui_key = 'largefiles'
336 largefiles.ui_key = 'largefiles'
336 largefiles.ui_value = ''
337 largefiles.ui_value = ''
337 self.sa.add(largefiles)
338 self.sa.add(largefiles)
338
339
339 # set default largefiles cache dir, defaults to
340 # set default largefiles cache dir, defaults to
340 # /repo_store_location/.cache/largefiles
341 # /repo_store_location/.cache/largefiles
341 largefiles = RhodeCodeUi()
342 largefiles = RhodeCodeUi()
342 largefiles.ui_section = 'largefiles'
343 largefiles.ui_section = 'largefiles'
343 largefiles.ui_key = 'usercache'
344 largefiles.ui_key = 'usercache'
344 largefiles.ui_value = largefiles_store(repo_store_path)
345 largefiles.ui_value = largefiles_store(repo_store_path)
345
346
346 self.sa.add(largefiles)
347 self.sa.add(largefiles)
347
348
349 # set default lfs cache dir, defaults to
350 # /repo_store_location/.cache/lfs_store
351 lfsstore = RhodeCodeUi()
352 lfsstore.ui_section = 'vcs_git_lfs'
353 lfsstore.ui_key = 'store_location'
354 lfsstore.ui_value = lfs_store(repo_store_path)
355
356 self.sa.add(lfsstore)
357
348 # enable hgsubversion disabled by default
358 # enable hgsubversion disabled by default
349 hgsubversion = RhodeCodeUi()
359 hgsubversion = RhodeCodeUi()
350 hgsubversion.ui_section = 'extensions'
360 hgsubversion.ui_section = 'extensions'
351 hgsubversion.ui_key = 'hgsubversion'
361 hgsubversion.ui_key = 'hgsubversion'
352 hgsubversion.ui_value = ''
362 hgsubversion.ui_value = ''
353 hgsubversion.ui_active = False
363 hgsubversion.ui_active = False
354 self.sa.add(hgsubversion)
364 self.sa.add(hgsubversion)
355
365
356 # enable hggit disabled by default
366 # enable hggit disabled by default
357 hggit = RhodeCodeUi()
367 hggit = RhodeCodeUi()
358 hggit.ui_section = 'extensions'
368 hggit.ui_section = 'extensions'
359 hggit.ui_key = 'hggit'
369 hggit.ui_key = 'hggit'
360 hggit.ui_value = ''
370 hggit.ui_value = ''
361 hggit.ui_active = False
371 hggit.ui_active = False
362 self.sa.add(hggit)
372 self.sa.add(hggit)
363
373
364 # set svn branch defaults
374 # set svn branch defaults
365 branches = ["/branches/*", "/trunk"]
375 branches = ["/branches/*", "/trunk"]
366 tags = ["/tags/*"]
376 tags = ["/tags/*"]
367
377
368 for branch in branches:
378 for branch in branches:
369 settings_model.create_ui_section_value(
379 settings_model.create_ui_section_value(
370 RhodeCodeUi.SVN_BRANCH_ID, branch)
380 RhodeCodeUi.SVN_BRANCH_ID, branch)
371
381
372 for tag in tags:
382 for tag in tags:
373 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
383 settings_model.create_ui_section_value(RhodeCodeUi.SVN_TAG_ID, tag)
374
384
375 def create_auth_plugin_options(self, skip_existing=False):
385 def create_auth_plugin_options(self, skip_existing=False):
376 """
386 """
377 Create default auth plugin settings, and make it active
387 Create default auth plugin settings, and make it active
378
388
379 :param skip_existing:
389 :param skip_existing:
380 """
390 """
381
391
382 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
392 for k, v, t in [('auth_plugins', 'egg:rhodecode-enterprise-ce#rhodecode', 'list'),
383 ('auth_rhodecode_enabled', 'True', 'bool')]:
393 ('auth_rhodecode_enabled', 'True', 'bool')]:
384 if (skip_existing and
394 if (skip_existing and
385 SettingsModel().get_setting_by_name(k) is not None):
395 SettingsModel().get_setting_by_name(k) is not None):
386 log.debug('Skipping option %s' % k)
396 log.debug('Skipping option %s' % k)
387 continue
397 continue
388 setting = RhodeCodeSetting(k, v, t)
398 setting = RhodeCodeSetting(k, v, t)
389 self.sa.add(setting)
399 self.sa.add(setting)
390
400
391 def create_default_options(self, skip_existing=False):
401 def create_default_options(self, skip_existing=False):
392 """Creates default settings"""
402 """Creates default settings"""
393
403
394 for k, v, t in [
404 for k, v, t in [
395 ('default_repo_enable_locking', False, 'bool'),
405 ('default_repo_enable_locking', False, 'bool'),
396 ('default_repo_enable_downloads', False, 'bool'),
406 ('default_repo_enable_downloads', False, 'bool'),
397 ('default_repo_enable_statistics', False, 'bool'),
407 ('default_repo_enable_statistics', False, 'bool'),
398 ('default_repo_private', False, 'bool'),
408 ('default_repo_private', False, 'bool'),
399 ('default_repo_type', 'hg', 'unicode')]:
409 ('default_repo_type', 'hg', 'unicode')]:
400
410
401 if (skip_existing and
411 if (skip_existing and
402 SettingsModel().get_setting_by_name(k) is not None):
412 SettingsModel().get_setting_by_name(k) is not None):
403 log.debug('Skipping option %s' % k)
413 log.debug('Skipping option %s' % k)
404 continue
414 continue
405 setting = RhodeCodeSetting(k, v, t)
415 setting = RhodeCodeSetting(k, v, t)
406 self.sa.add(setting)
416 self.sa.add(setting)
407
417
408 def fixup_groups(self):
418 def fixup_groups(self):
409 def_usr = User.get_default_user()
419 def_usr = User.get_default_user()
410 for g in RepoGroup.query().all():
420 for g in RepoGroup.query().all():
411 g.group_name = g.get_new_name(g.name)
421 g.group_name = g.get_new_name(g.name)
412 self.sa.add(g)
422 self.sa.add(g)
413 # get default perm
423 # get default perm
414 default = UserRepoGroupToPerm.query()\
424 default = UserRepoGroupToPerm.query()\
415 .filter(UserRepoGroupToPerm.group == g)\
425 .filter(UserRepoGroupToPerm.group == g)\
416 .filter(UserRepoGroupToPerm.user == def_usr)\
426 .filter(UserRepoGroupToPerm.user == def_usr)\
417 .scalar()
427 .scalar()
418
428
419 if default is None:
429 if default is None:
420 log.debug('missing default permission for group %s adding' % g)
430 log.debug('missing default permission for group %s adding' % g)
421 perm_obj = RepoGroupModel()._create_default_perms(g)
431 perm_obj = RepoGroupModel()._create_default_perms(g)
422 self.sa.add(perm_obj)
432 self.sa.add(perm_obj)
423
433
424 def reset_permissions(self, username):
434 def reset_permissions(self, username):
425 """
435 """
426 Resets permissions to default state, useful when old systems had
436 Resets permissions to default state, useful when old systems had
427 bad permissions, we must clean them up
437 bad permissions, we must clean them up
428
438
429 :param username:
439 :param username:
430 """
440 """
431 default_user = User.get_by_username(username)
441 default_user = User.get_by_username(username)
432 if not default_user:
442 if not default_user:
433 return
443 return
434
444
435 u2p = UserToPerm.query()\
445 u2p = UserToPerm.query()\
436 .filter(UserToPerm.user == default_user).all()
446 .filter(UserToPerm.user == default_user).all()
437 fixed = False
447 fixed = False
438 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
448 if len(u2p) != len(Permission.DEFAULT_USER_PERMISSIONS):
439 for p in u2p:
449 for p in u2p:
440 Session().delete(p)
450 Session().delete(p)
441 fixed = True
451 fixed = True
442 self.populate_default_permissions()
452 self.populate_default_permissions()
443 return fixed
453 return fixed
444
454
445 def update_repo_info(self):
455 def update_repo_info(self):
446 RepoModel.update_repoinfo()
456 RepoModel.update_repoinfo()
447
457
448 def config_prompt(self, test_repo_path='', retries=3):
458 def config_prompt(self, test_repo_path='', retries=3):
449 defaults = self.cli_args
459 defaults = self.cli_args
450 _path = defaults.get('repos_location')
460 _path = defaults.get('repos_location')
451 if retries == 3:
461 if retries == 3:
452 log.info('Setting up repositories config')
462 log.info('Setting up repositories config')
453
463
454 if _path is not None:
464 if _path is not None:
455 path = _path
465 path = _path
456 elif not self.tests and not test_repo_path:
466 elif not self.tests and not test_repo_path:
457 path = raw_input(
467 path = raw_input(
458 'Enter a valid absolute path to store repositories. '
468 'Enter a valid absolute path to store repositories. '
459 'All repositories in that path will be added automatically:'
469 'All repositories in that path will be added automatically:'
460 )
470 )
461 else:
471 else:
462 path = test_repo_path
472 path = test_repo_path
463 path_ok = True
473 path_ok = True
464
474
465 # check proper dir
475 # check proper dir
466 if not os.path.isdir(path):
476 if not os.path.isdir(path):
467 path_ok = False
477 path_ok = False
468 log.error('Given path %s is not a valid directory' % (path,))
478 log.error('Given path %s is not a valid directory' % (path,))
469
479
470 elif not os.path.isabs(path):
480 elif not os.path.isabs(path):
471 path_ok = False
481 path_ok = False
472 log.error('Given path %s is not an absolute path' % (path,))
482 log.error('Given path %s is not an absolute path' % (path,))
473
483
474 # check if path is at least readable.
484 # check if path is at least readable.
475 if not os.access(path, os.R_OK):
485 if not os.access(path, os.R_OK):
476 path_ok = False
486 path_ok = False
477 log.error('Given path %s is not readable' % (path,))
487 log.error('Given path %s is not readable' % (path,))
478
488
479 # check write access, warn user about non writeable paths
489 # check write access, warn user about non writeable paths
480 elif not os.access(path, os.W_OK) and path_ok:
490 elif not os.access(path, os.W_OK) and path_ok:
481 log.warning('No write permission to given path %s' % (path,))
491 log.warning('No write permission to given path %s' % (path,))
482
492
483 q = ('Given path %s is not writeable, do you want to '
493 q = ('Given path %s is not writeable, do you want to '
484 'continue with read only mode ? [y/n]' % (path,))
494 'continue with read only mode ? [y/n]' % (path,))
485 if not self.ask_ok(q):
495 if not self.ask_ok(q):
486 log.error('Canceled by user')
496 log.error('Canceled by user')
487 sys.exit(-1)
497 sys.exit(-1)
488
498
489 if retries == 0:
499 if retries == 0:
490 sys.exit('max retries reached')
500 sys.exit('max retries reached')
491 if not path_ok:
501 if not path_ok:
492 retries -= 1
502 retries -= 1
493 return self.config_prompt(test_repo_path, retries)
503 return self.config_prompt(test_repo_path, retries)
494
504
495 real_path = os.path.normpath(os.path.realpath(path))
505 real_path = os.path.normpath(os.path.realpath(path))
496
506
497 if real_path != os.path.normpath(path):
507 if real_path != os.path.normpath(path):
498 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
508 q = ('Path looks like a symlink, RhodeCode Enterprise will store '
499 'given path as %s ? [y/n]') % (real_path,)
509 'given path as %s ? [y/n]') % (real_path,)
500 if not self.ask_ok(q):
510 if not self.ask_ok(q):
501 log.error('Canceled by user')
511 log.error('Canceled by user')
502 sys.exit(-1)
512 sys.exit(-1)
503
513
504 return real_path
514 return real_path
505
515
506 def create_settings(self, path):
516 def create_settings(self, path):
507
517
508 self.create_ui_settings(path)
518 self.create_ui_settings(path)
509
519
510 ui_config = [
520 ui_config = [
511 ('web', 'push_ssl', 'false'),
521 ('web', 'push_ssl', 'false'),
512 ('web', 'allow_archive', 'gz zip bz2'),
522 ('web', 'allow_archive', 'gz zip bz2'),
513 ('web', 'allow_push', '*'),
523 ('web', 'allow_push', '*'),
514 ('web', 'baseurl', '/'),
524 ('web', 'baseurl', '/'),
515 ('paths', '/', path),
525 ('paths', '/', path),
516 ('phases', 'publish', 'true')
526 ('phases', 'publish', 'true')
517 ]
527 ]
518 for section, key, value in ui_config:
528 for section, key, value in ui_config:
519 ui_conf = RhodeCodeUi()
529 ui_conf = RhodeCodeUi()
520 setattr(ui_conf, 'ui_section', section)
530 setattr(ui_conf, 'ui_section', section)
521 setattr(ui_conf, 'ui_key', key)
531 setattr(ui_conf, 'ui_key', key)
522 setattr(ui_conf, 'ui_value', value)
532 setattr(ui_conf, 'ui_value', value)
523 self.sa.add(ui_conf)
533 self.sa.add(ui_conf)
524
534
525 # rhodecode app settings
535 # rhodecode app settings
526 settings = [
536 settings = [
527 ('realm', 'RhodeCode', 'unicode'),
537 ('realm', 'RhodeCode', 'unicode'),
528 ('title', '', 'unicode'),
538 ('title', '', 'unicode'),
529 ('pre_code', '', 'unicode'),
539 ('pre_code', '', 'unicode'),
530 ('post_code', '', 'unicode'),
540 ('post_code', '', 'unicode'),
531 ('show_public_icon', True, 'bool'),
541 ('show_public_icon', True, 'bool'),
532 ('show_private_icon', True, 'bool'),
542 ('show_private_icon', True, 'bool'),
533 ('stylify_metatags', False, 'bool'),
543 ('stylify_metatags', False, 'bool'),
534 ('dashboard_items', 100, 'int'),
544 ('dashboard_items', 100, 'int'),
535 ('admin_grid_items', 25, 'int'),
545 ('admin_grid_items', 25, 'int'),
536 ('show_version', True, 'bool'),
546 ('show_version', True, 'bool'),
537 ('use_gravatar', False, 'bool'),
547 ('use_gravatar', False, 'bool'),
538 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
548 ('gravatar_url', User.DEFAULT_GRAVATAR_URL, 'unicode'),
539 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
549 ('clone_uri_tmpl', Repository.DEFAULT_CLONE_URI, 'unicode'),
540 ('support_url', '', 'unicode'),
550 ('support_url', '', 'unicode'),
541 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
551 ('update_url', RhodeCodeSetting.DEFAULT_UPDATE_URL, 'unicode'),
542 ('show_revision_number', True, 'bool'),
552 ('show_revision_number', True, 'bool'),
543 ('show_sha_length', 12, 'int'),
553 ('show_sha_length', 12, 'int'),
544 ]
554 ]
545
555
546 for key, val, type_ in settings:
556 for key, val, type_ in settings:
547 sett = RhodeCodeSetting(key, val, type_)
557 sett = RhodeCodeSetting(key, val, type_)
548 self.sa.add(sett)
558 self.sa.add(sett)
549
559
550 self.create_auth_plugin_options()
560 self.create_auth_plugin_options()
551 self.create_default_options()
561 self.create_default_options()
552
562
553 log.info('created ui config')
563 log.info('created ui config')
554
564
555 def create_user(self, username, password, email='', admin=False,
565 def create_user(self, username, password, email='', admin=False,
556 strict_creation_check=True, api_key=None):
566 strict_creation_check=True, api_key=None):
557 log.info('creating user %s' % username)
567 log.info('creating user %s' % username)
558 user = UserModel().create_or_update(
568 user = UserModel().create_or_update(
559 username, password, email, firstname='RhodeCode', lastname='Admin',
569 username, password, email, firstname='RhodeCode', lastname='Admin',
560 active=True, admin=admin, extern_type="rhodecode",
570 active=True, admin=admin, extern_type="rhodecode",
561 strict_creation_check=strict_creation_check)
571 strict_creation_check=strict_creation_check)
562
572
563 if api_key:
573 if api_key:
564 log.info('setting a provided api key for the user %s', username)
574 log.info('setting a provided api key for the user %s', username)
565 from rhodecode.model.auth_token import AuthTokenModel
575 from rhodecode.model.auth_token import AuthTokenModel
566 AuthTokenModel().create(
576 AuthTokenModel().create(
567 user=user, description='BUILTIN TOKEN')
577 user=user, description='BUILTIN TOKEN')
568
578
569 def create_default_user(self):
579 def create_default_user(self):
570 log.info('creating default user')
580 log.info('creating default user')
571 # create default user for handling default permissions.
581 # create default user for handling default permissions.
572 user = UserModel().create_or_update(username=User.DEFAULT_USER,
582 user = UserModel().create_or_update(username=User.DEFAULT_USER,
573 password=str(uuid.uuid1())[:20],
583 password=str(uuid.uuid1())[:20],
574 email=User.DEFAULT_USER_EMAIL,
584 email=User.DEFAULT_USER_EMAIL,
575 firstname='Anonymous',
585 firstname='Anonymous',
576 lastname='User',
586 lastname='User',
577 strict_creation_check=False)
587 strict_creation_check=False)
578 # based on configuration options activate/deactive this user which
588 # based on configuration options activate/deactive this user which
579 # controlls anonymous access
589 # controlls anonymous access
580 if self.cli_args.get('public_access') is False:
590 if self.cli_args.get('public_access') is False:
581 log.info('Public access disabled')
591 log.info('Public access disabled')
582 user.active = False
592 user.active = False
583 Session().add(user)
593 Session().add(user)
584 Session().commit()
594 Session().commit()
585
595
586 def create_permissions(self):
596 def create_permissions(self):
587 """
597 """
588 Creates all permissions defined in the system
598 Creates all permissions defined in the system
589 """
599 """
590 # module.(access|create|change|delete)_[name]
600 # module.(access|create|change|delete)_[name]
591 # module.(none|read|write|admin)
601 # module.(none|read|write|admin)
592 log.info('creating permissions')
602 log.info('creating permissions')
593 PermissionModel(self.sa).create_permissions()
603 PermissionModel(self.sa).create_permissions()
594
604
595 def populate_default_permissions(self):
605 def populate_default_permissions(self):
596 """
606 """
597 Populate default permissions. It will create only the default
607 Populate default permissions. It will create only the default
598 permissions that are missing, and not alter already defined ones
608 permissions that are missing, and not alter already defined ones
599 """
609 """
600 log.info('creating default user permissions')
610 log.info('creating default user permissions')
601 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
611 PermissionModel(self.sa).create_default_user_permissions(user=User.DEFAULT_USER)
@@ -1,49 +1,56 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2017 RhodeCode GmbH
3 # Copyright (C) 2014-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 GIT module
22 GIT module
23 """
23 """
24
24 import os
25 import logging
25 import logging
26
26
27 from rhodecode.lib.vcs import connection
27 from rhodecode.lib.vcs import connection
28 from rhodecode.lib.vcs.backends.git.repository import GitRepository
28 from rhodecode.lib.vcs.backends.git.repository import GitRepository
29 from rhodecode.lib.vcs.backends.git.commit import GitCommit
29 from rhodecode.lib.vcs.backends.git.commit import GitCommit
30 from rhodecode.lib.vcs.backends.git.inmemory import GitInMemoryCommit
30 from rhodecode.lib.vcs.backends.git.inmemory import GitInMemoryCommit
31
31
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35
35
36 def discover_git_version(raise_on_exc=False):
36 def discover_git_version(raise_on_exc=False):
37 """
37 """
38 Returns the string as it was returned by running 'git --version'
38 Returns the string as it was returned by running 'git --version'
39
39
40 It will return an empty string in case the connection is not initialized
40 It will return an empty string in case the connection is not initialized
41 or no vcsserver is available.
41 or no vcsserver is available.
42 """
42 """
43 try:
43 try:
44 return connection.Git.discover_git_version()
44 return connection.Git.discover_git_version()
45 except Exception:
45 except Exception:
46 log.warning("Failed to discover the Git version", exc_info=True)
46 log.warning("Failed to discover the Git version", exc_info=True)
47 if raise_on_exc:
47 if raise_on_exc:
48 raise
48 raise
49 return ''
49 return ''
50
51
52 def lfs_store(base_location):
53 """
54 Return a lfs store relative to base_location
55 """
56 return os.path.join(base_location, '.cache', 'lfs_store')
@@ -1,282 +1,287 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 from subprocess32 import Popen, PIPE
21 from subprocess32 import Popen, PIPE
22 import os
22 import os
23 import shutil
23 import shutil
24 import sys
24 import sys
25 import tempfile
25 import tempfile
26
26
27 import pytest
27 import pytest
28 from sqlalchemy.engine import url
28 from sqlalchemy.engine import url
29
29
30 from rhodecode.tests.fixture import TestINI
30 from rhodecode.tests.fixture import TestINI
31
31
32
32
33 def _get_dbs_from_metafunc(metafunc):
33 def _get_dbs_from_metafunc(metafunc):
34 if hasattr(metafunc.function, 'dbs'):
34 if hasattr(metafunc.function, 'dbs'):
35 # Supported backends by this test function, created from
35 # Supported backends by this test function, created from
36 # pytest.mark.dbs
36 # pytest.mark.dbs
37 backends = metafunc.function.dbs.args
37 backends = metafunc.function.dbs.args
38 else:
38 else:
39 backends = metafunc.config.getoption('--dbs')
39 backends = metafunc.config.getoption('--dbs')
40 return backends
40 return backends
41
41
42
42
43 def pytest_generate_tests(metafunc):
43 def pytest_generate_tests(metafunc):
44 # Support test generation based on --dbs parameter
44 # Support test generation based on --dbs parameter
45 if 'db_backend' in metafunc.fixturenames:
45 if 'db_backend' in metafunc.fixturenames:
46 requested_backends = set(metafunc.config.getoption('--dbs'))
46 requested_backends = set(metafunc.config.getoption('--dbs'))
47 backends = _get_dbs_from_metafunc(metafunc)
47 backends = _get_dbs_from_metafunc(metafunc)
48 backends = requested_backends.intersection(backends)
48 backends = requested_backends.intersection(backends)
49 # TODO: johbo: Disabling a backend did not work out with
49 # TODO: johbo: Disabling a backend did not work out with
50 # parametrization, find better way to achieve this.
50 # parametrization, find better way to achieve this.
51 if not backends:
51 if not backends:
52 metafunc.function._skip = True
52 metafunc.function._skip = True
53 metafunc.parametrize('db_backend_name', backends)
53 metafunc.parametrize('db_backend_name', backends)
54
54
55
55
56 def pytest_collection_modifyitems(session, config, items):
56 def pytest_collection_modifyitems(session, config, items):
57 remaining = [
57 remaining = [
58 i for i in items if not getattr(i.obj, '_skip', False)]
58 i for i in items if not getattr(i.obj, '_skip', False)]
59 items[:] = remaining
59 items[:] = remaining
60
60
61
61
62 @pytest.fixture
62 @pytest.fixture
63 def db_backend(
63 def db_backend(
64 request, db_backend_name, pylons_config, tmpdir_factory):
64 request, db_backend_name, pylons_config, tmpdir_factory):
65 basetemp = tmpdir_factory.getbasetemp().strpath
65 basetemp = tmpdir_factory.getbasetemp().strpath
66 klass = _get_backend(db_backend_name)
66 klass = _get_backend(db_backend_name)
67
67
68 option_name = '--{}-connection-string'.format(db_backend_name)
68 option_name = '--{}-connection-string'.format(db_backend_name)
69 connection_string = request.config.getoption(option_name) or None
69 connection_string = request.config.getoption(option_name) or None
70
70
71 return klass(
71 return klass(
72 config_file=pylons_config, basetemp=basetemp,
72 config_file=pylons_config, basetemp=basetemp,
73 connection_string=connection_string)
73 connection_string=connection_string)
74
74
75
75
76 def _get_backend(backend_type):
76 def _get_backend(backend_type):
77 return {
77 return {
78 'sqlite': SQLiteDBBackend,
78 'sqlite': SQLiteDBBackend,
79 'postgres': PostgresDBBackend,
79 'postgres': PostgresDBBackend,
80 'mysql': MySQLDBBackend,
80 'mysql': MySQLDBBackend,
81 '': EmptyDBBackend
81 '': EmptyDBBackend
82 }[backend_type]
82 }[backend_type]
83
83
84
84
85 class DBBackend(object):
85 class DBBackend(object):
86 _store = os.path.dirname(os.path.abspath(__file__))
86 _store = os.path.dirname(os.path.abspath(__file__))
87 _type = None
87 _type = None
88 _base_ini_config = [{'app:main': {'vcs.start_server': 'false'}}]
88 _base_ini_config = [{'app:main': {'vcs.start_server': 'false'}}]
89 _db_url = [{'app:main': {'sqlalchemy.db1.url': ''}}]
89 _db_url = [{'app:main': {'sqlalchemy.db1.url': ''}}]
90 _base_db_name = 'rhodecode_test_db_backend'
90 _base_db_name = 'rhodecode_test_db_backend'
91
91
92 def __init__(
92 def __init__(
93 self, config_file, db_name=None, basetemp=None,
93 self, config_file, db_name=None, basetemp=None,
94 connection_string=None):
94 connection_string=None):
95
95
96 from rhodecode.lib.vcs.backends.hg import largefiles_store
96 from rhodecode.lib.vcs.backends.hg import largefiles_store
97 from rhodecode.lib.vcs.backends.git import lfs_store
97
98
98 self.fixture_store = os.path.join(self._store, self._type)
99 self.fixture_store = os.path.join(self._store, self._type)
99 self.db_name = db_name or self._base_db_name
100 self.db_name = db_name or self._base_db_name
100 self._base_ini_file = config_file
101 self._base_ini_file = config_file
101 self.stderr = ''
102 self.stderr = ''
102 self.stdout = ''
103 self.stdout = ''
103 self._basetemp = basetemp or tempfile.gettempdir()
104 self._basetemp = basetemp or tempfile.gettempdir()
104 self._repos_location = os.path.join(self._basetemp, 'rc_test_repos')
105 self._repos_location = os.path.join(self._basetemp, 'rc_test_repos')
105 self._repos_hg_largefiles_store = largefiles_store(self._basetemp)
106 self._repos_hg_largefiles_store = largefiles_store(self._basetemp)
107 self._repos_git_lfs_store = lfs_store(self._basetemp)
106 self.connection_string = connection_string
108 self.connection_string = connection_string
107
109
108 @property
110 @property
109 def connection_string(self):
111 def connection_string(self):
110 return self._connection_string
112 return self._connection_string
111
113
112 @connection_string.setter
114 @connection_string.setter
113 def connection_string(self, new_connection_string):
115 def connection_string(self, new_connection_string):
114 if not new_connection_string:
116 if not new_connection_string:
115 new_connection_string = self.get_default_connection_string()
117 new_connection_string = self.get_default_connection_string()
116 else:
118 else:
117 new_connection_string = new_connection_string.format(
119 new_connection_string = new_connection_string.format(
118 db_name=self.db_name)
120 db_name=self.db_name)
119 url_parts = url.make_url(new_connection_string)
121 url_parts = url.make_url(new_connection_string)
120 self._connection_string = new_connection_string
122 self._connection_string = new_connection_string
121 self.user = url_parts.username
123 self.user = url_parts.username
122 self.password = url_parts.password
124 self.password = url_parts.password
123 self.host = url_parts.host
125 self.host = url_parts.host
124
126
125 def get_default_connection_string(self):
127 def get_default_connection_string(self):
126 raise NotImplementedError('default connection_string is required.')
128 raise NotImplementedError('default connection_string is required.')
127
129
128 def execute(self, cmd, env=None, *args):
130 def execute(self, cmd, env=None, *args):
129 """
131 """
130 Runs command on the system with given ``args``.
132 Runs command on the system with given ``args``.
131 """
133 """
132
134
133 command = cmd + ' ' + ' '.join(args)
135 command = cmd + ' ' + ' '.join(args)
134 sys.stdout.write(command)
136 sys.stdout.write(command)
135
137
136 # Tell Python to use UTF-8 encoding out stdout
138 # Tell Python to use UTF-8 encoding out stdout
137 _env = os.environ.copy()
139 _env = os.environ.copy()
138 _env['PYTHONIOENCODING'] = 'UTF-8'
140 _env['PYTHONIOENCODING'] = 'UTF-8'
139 if env:
141 if env:
140 _env.update(env)
142 _env.update(env)
141 self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env)
143 self.p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, env=_env)
142 self.stdout, self.stderr = self.p.communicate()
144 self.stdout, self.stderr = self.p.communicate()
143 sys.stdout.write('COMMAND:'+command+'\n')
145 sys.stdout.write('COMMAND:'+command+'\n')
144 sys.stdout.write(self.stdout)
146 sys.stdout.write(self.stdout)
145 return self.stdout, self.stderr
147 return self.stdout, self.stderr
146
148
147 def assert_returncode_success(self):
149 def assert_returncode_success(self):
148 if not self.p.returncode == 0:
150 if not self.p.returncode == 0:
149 print(self.stderr)
151 print(self.stderr)
150 raise AssertionError('non 0 retcode:{}'.format(self.p.returncode))
152 raise AssertionError('non 0 retcode:{}'.format(self.p.returncode))
151
153
152 def setup_rhodecode_db(self, ini_params=None, env=None):
154 def setup_rhodecode_db(self, ini_params=None, env=None):
153 if not ini_params:
155 if not ini_params:
154 ini_params = self._base_ini_config
156 ini_params = self._base_ini_config
155
157
156 ini_params.extend(self._db_url)
158 ini_params.extend(self._db_url)
157 with TestINI(self._base_ini_file, ini_params,
159 with TestINI(self._base_ini_file, ini_params,
158 self._type, destroy=True) as _ini_file:
160 self._type, destroy=True) as _ini_file:
161
159 if not os.path.isdir(self._repos_location):
162 if not os.path.isdir(self._repos_location):
160 os.makedirs(self._repos_location)
163 os.makedirs(self._repos_location)
161 if not os.path.isdir(self._repos_hg_largefiles_store):
164 if not os.path.isdir(self._repos_hg_largefiles_store):
162 os.makedirs(self._repos_hg_largefiles_store)
165 os.makedirs(self._repos_hg_largefiles_store)
166 if not os.path.isdir(self._repos_git_lfs_store):
167 os.makedirs(self._repos_git_lfs_store)
163
168
164 self.execute(
169 self.execute(
165 "paster setup-rhodecode {0} --user=marcink "
170 "paster setup-rhodecode {0} --user=marcink "
166 "--email=marcin@rhodeocode.com --password={1} "
171 "--email=marcin@rhodeocode.com --password={1} "
167 "--repos={2} --force-yes".format(
172 "--repos={2} --force-yes".format(
168 _ini_file, 'qweqwe', self._repos_location), env=env)
173 _ini_file, 'qweqwe', self._repos_location), env=env)
169
174
170 def upgrade_database(self, ini_params=None):
175 def upgrade_database(self, ini_params=None):
171 if not ini_params:
176 if not ini_params:
172 ini_params = self._base_ini_config
177 ini_params = self._base_ini_config
173 ini_params.extend(self._db_url)
178 ini_params.extend(self._db_url)
174
179
175 test_ini = TestINI(
180 test_ini = TestINI(
176 self._base_ini_file, ini_params, self._type, destroy=True)
181 self._base_ini_file, ini_params, self._type, destroy=True)
177 with test_ini as ini_file:
182 with test_ini as ini_file:
178 if not os.path.isdir(self._repos_location):
183 if not os.path.isdir(self._repos_location):
179 os.makedirs(self._repos_location)
184 os.makedirs(self._repos_location)
180 self.execute(
185 self.execute(
181 "paster upgrade-db {} --force-yes".format(ini_file))
186 "paster upgrade-db {} --force-yes".format(ini_file))
182
187
183 def setup_db(self):
188 def setup_db(self):
184 raise NotImplementedError
189 raise NotImplementedError
185
190
186 def teardown_db(self):
191 def teardown_db(self):
187 raise NotImplementedError
192 raise NotImplementedError
188
193
189 def import_dump(self, dumpname):
194 def import_dump(self, dumpname):
190 raise NotImplementedError
195 raise NotImplementedError
191
196
192
197
193 class EmptyDBBackend(DBBackend):
198 class EmptyDBBackend(DBBackend):
194 _type = ''
199 _type = ''
195
200
196 def setup_db(self):
201 def setup_db(self):
197 pass
202 pass
198
203
199 def teardown_db(self):
204 def teardown_db(self):
200 pass
205 pass
201
206
202 def import_dump(self, dumpname):
207 def import_dump(self, dumpname):
203 pass
208 pass
204
209
205 def assert_returncode_success(self):
210 def assert_returncode_success(self):
206 assert True
211 assert True
207
212
208
213
209 class SQLiteDBBackend(DBBackend):
214 class SQLiteDBBackend(DBBackend):
210 _type = 'sqlite'
215 _type = 'sqlite'
211
216
212 def get_default_connection_string(self):
217 def get_default_connection_string(self):
213 return 'sqlite:///{}/{}.sqlite'.format(self._basetemp, self.db_name)
218 return 'sqlite:///{}/{}.sqlite'.format(self._basetemp, self.db_name)
214
219
215 def setup_db(self):
220 def setup_db(self):
216 # dump schema for tests
221 # dump schema for tests
217 # cp -v $TEST_DB_NAME
222 # cp -v $TEST_DB_NAME
218 self._db_url = [{'app:main': {
223 self._db_url = [{'app:main': {
219 'sqlalchemy.db1.url': self.connection_string}}]
224 'sqlalchemy.db1.url': self.connection_string}}]
220
225
221 def import_dump(self, dumpname):
226 def import_dump(self, dumpname):
222 dump = os.path.join(self.fixture_store, dumpname)
227 dump = os.path.join(self.fixture_store, dumpname)
223 shutil.copy(
228 shutil.copy(
224 dump,
229 dump,
225 os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self)))
230 os.path.join(self._basetemp, '{0.db_name}.sqlite'.format(self)))
226
231
227 def teardown_db(self):
232 def teardown_db(self):
228 self.execute("rm -rf {}.sqlite".format(
233 self.execute("rm -rf {}.sqlite".format(
229 os.path.join(self._basetemp, self.db_name)))
234 os.path.join(self._basetemp, self.db_name)))
230
235
231
236
232 class MySQLDBBackend(DBBackend):
237 class MySQLDBBackend(DBBackend):
233 _type = 'mysql'
238 _type = 'mysql'
234
239
235 def get_default_connection_string(self):
240 def get_default_connection_string(self):
236 return 'mysql://root:qweqwe@127.0.0.1/{}'.format(self.db_name)
241 return 'mysql://root:qweqwe@127.0.0.1/{}'.format(self.db_name)
237
242
238 def setup_db(self):
243 def setup_db(self):
239 # dump schema for tests
244 # dump schema for tests
240 # mysqldump -uroot -pqweqwe $TEST_DB_NAME
245 # mysqldump -uroot -pqweqwe $TEST_DB_NAME
241 self._db_url = [{'app:main': {
246 self._db_url = [{'app:main': {
242 'sqlalchemy.db1.url': self.connection_string}}]
247 'sqlalchemy.db1.url': self.connection_string}}]
243 self.execute("mysql -v -u{} -p{} -e 'create database '{}';'".format(
248 self.execute("mysql -v -u{} -p{} -e 'create database '{}';'".format(
244 self.user, self.password, self.db_name))
249 self.user, self.password, self.db_name))
245
250
246 def import_dump(self, dumpname):
251 def import_dump(self, dumpname):
247 dump = os.path.join(self.fixture_store, dumpname)
252 dump = os.path.join(self.fixture_store, dumpname)
248 self.execute("mysql -u{} -p{} {} < {}".format(
253 self.execute("mysql -u{} -p{} {} < {}".format(
249 self.user, self.password, self.db_name, dump))
254 self.user, self.password, self.db_name, dump))
250
255
251 def teardown_db(self):
256 def teardown_db(self):
252 self.execute("mysql -v -u{} -p{} -e 'drop database '{}';'".format(
257 self.execute("mysql -v -u{} -p{} -e 'drop database '{}';'".format(
253 self.user, self.password, self.db_name))
258 self.user, self.password, self.db_name))
254
259
255
260
256 class PostgresDBBackend(DBBackend):
261 class PostgresDBBackend(DBBackend):
257 _type = 'postgres'
262 _type = 'postgres'
258
263
259 def get_default_connection_string(self):
264 def get_default_connection_string(self):
260 return 'postgresql://postgres:qweqwe@localhost/{}'.format(self.db_name)
265 return 'postgresql://postgres:qweqwe@localhost/{}'.format(self.db_name)
261
266
262 def setup_db(self):
267 def setup_db(self):
263 # dump schema for tests
268 # dump schema for tests
264 # pg_dump -U postgres -h localhost $TEST_DB_NAME
269 # pg_dump -U postgres -h localhost $TEST_DB_NAME
265 self._db_url = [{'app:main': {
270 self._db_url = [{'app:main': {
266 'sqlalchemy.db1.url':
271 'sqlalchemy.db1.url':
267 self.connection_string}}]
272 self.connection_string}}]
268 self.execute("PGPASSWORD={} psql -U {} -h localhost "
273 self.execute("PGPASSWORD={} psql -U {} -h localhost "
269 "-c 'create database '{}';'".format(
274 "-c 'create database '{}';'".format(
270 self.password, self.user, self.db_name))
275 self.password, self.user, self.db_name))
271
276
272 def teardown_db(self):
277 def teardown_db(self):
273 self.execute("PGPASSWORD={} psql -U {} -h localhost "
278 self.execute("PGPASSWORD={} psql -U {} -h localhost "
274 "-c 'drop database if exists '{}';'".format(
279 "-c 'drop database if exists '{}';'".format(
275 self.password, self.user, self.db_name))
280 self.password, self.user, self.db_name))
276
281
277 def import_dump(self, dumpname):
282 def import_dump(self, dumpname):
278 dump = os.path.join(self.fixture_store, dumpname)
283 dump = os.path.join(self.fixture_store, dumpname)
279 self.execute(
284 self.execute(
280 "PGPASSWORD={} psql -U {} -h localhost -d {} -1 "
285 "PGPASSWORD={} psql -U {} -h localhost -d {} -1 "
281 "-f {}".format(
286 "-f {}".format(
282 self.password, self.user, self.db_name, dump))
287 self.password, self.user, self.db_name, dump))
General Comments 0
You need to be logged in to leave comments. Login now