##// END OF EJS Templates
svn-support: Add subscriber to execute the svn.proxy.reload_cmd on config changes. #4271
Martin Bornhold -
r1011:0a4f6193 default
parent child Browse files
Show More
@@ -1,69 +1,81 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22 import os
23 23
24 from rhodecode import events
24 # Do not use `from rhodecode import events` here, it will be overridden by the
25 # events module in this package due to pythons import mechanism.
26 from rhodecode.events import RepoGroupEvent
25 27 from rhodecode.config.middleware import _bool_setting, _string_setting
26 28
27 from .subscribers import generate_config_subscriber
29 from .events import ModDavSvnConfigChange
30 from .subscribers import generate_config_subscriber, AsyncSubprocessSubscriber
28 31 from . import config_keys
29 32
30 33
31 34 log = logging.getLogger(__name__)
32 35
33 36
34 37 def includeme(config):
35 38 settings = config.registry.settings
36 39 _sanitize_settings_and_apply_defaults(settings)
37 40
38 41 if settings[config_keys.generate_config]:
42 # Add subscriber to generate the Apache mod dav svn configuration on
43 # repository group events.
44 config.add_subscriber(generate_config_subscriber, RepoGroupEvent)
45
46 # Prepare reload command to pass it to the subprocess module and add a
47 # subscriber to execute it on configuration changes.
48 cmd = settings[config_keys.reload_command]
49 cmd = cmd.split(' ') if cmd else cmd
39 50 config.add_subscriber(
40 generate_config_subscriber, events.RepoGroupEvent)
51 AsyncSubprocessSubscriber(cmd=cmd, timeout=5),
52 ModDavSvnConfigChange)
41 53
42 54
43 55 def _sanitize_settings_and_apply_defaults(settings):
44 56 """
45 57 Set defaults, convert to python types and validate settings.
46 58 """
47 59 # Convert bool settings from string to bool.
48 60 _bool_setting(settings, config_keys.generate_config, 'false')
49 61 _bool_setting(settings, config_keys.list_parent_path, 'true')
50 62 _string_setting(settings, config_keys.config_file_path, '', lower=False)
51 63 _string_setting(settings, config_keys.location_root, '/', lower=False)
52 64 _string_setting(settings, config_keys.reload_command, '', lower=False)
53 65
54 66 # Append path separator to location root.
55 67 settings[config_keys.location_root] = _append_path_sep(
56 68 settings[config_keys.location_root])
57 69
58 70 # Validate settings.
59 71 if settings[config_keys.generate_config]:
60 72 assert len(settings[config_keys.config_file_path]) > 0
61 73
62 74
63 75 def _append_path_sep(path):
64 76 """
65 77 Append the path separator if missing.
66 78 """
67 79 if isinstance(path, basestring) and not path.endswith(os.path.sep):
68 80 path += os.path.sep
69 81 return path
@@ -1,42 +1,132 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 import subprocess32
23 from threading import Thread
22 24
23 from rhodecode.lib.utils import get_rhodecode_base_path
25
24 26 from .utils import generate_mod_dav_svn_config
25 27
26 28
27 29 log = logging.getLogger(__name__)
28 30
29 31
30 32 def generate_config_subscriber(event):
31 33 """
32 34 Subscriber to the `rhodcode.events.RepoGroupEvent`. This triggers the
33 35 automatic generation of mod_dav_svn config file on repository group
34 36 changes.
35 37 """
36 38 try:
37 generate_mod_dav_svn_config(
38 settings=event.request.registry.settings,
39 parent_path_root=get_rhodecode_base_path())
39 generate_mod_dav_svn_config(event.request.registry)
40 40 except Exception:
41 41 log.exception(
42 42 'Exception while generating subversion mod_dav_svn configuration.')
43
44
45 class Subscriber(object):
46 def __call__(self, event):
47 self.run(event)
48
49 def run(self, event):
50 raise NotImplementedError('Subclass has to implement this.')
51
52
53 class AsyncSubscriber(Subscriber):
54 def __init__(self, *args, **kwargs):
55 self._init_args = args
56 self._init_kwargs = kwargs
57
58 def __call__(self, event):
59 kwargs = {'event': event}
60 kwargs.update(self._init_kwargs)
61 self.thread = Thread(
62 target=self.run, args=self._init_args, kwargs=kwargs)
63 self.thread.start()
64
65
66 class AsyncSubprocessSubscriber(AsyncSubscriber):
67 def run(self, event, cmd, timeout=None):
68 log.debug('Executing command %s.', cmd)
69 try:
70 output = subprocess32.check_output(
71 cmd, timeout=timeout, stderr=subprocess32.STDOUT)
72 log.debug('Command finished %s', cmd)
73 if output:
74 log.debug('Command output: %s', output)
75 except subprocess32.TimeoutExpired as e:
76 log.exception('Timeout while executing command.')
77 if e.output:
78 log.error('Command output: %s', e.output)
79 except subprocess32.CalledProcessError as e:
80 log.exception('Error while executing command.')
81 if e.output:
82 log.error('Command output: %s', e.output)
83 except:
84 log.exception(
85 'Exception while executing command %s.', cmd)
86
87
88 # class ReloadApacheSubscriber(object):
89 # """
90 # Subscriber to pyramids event system. It executes the Apache reload command
91 # if set in ini-file. The command is executed asynchronously in a separate
92 # task. This is done to prevent a delay of the function which triggered the
93 # event in case of a longer running command. If a timeout is passed to the
94 # constructor the command will be terminated after expiration.
95 # """
96 # def __init__(self, settings, timeout=None):
97 # self.thread = None
98 # cmd = self.get_command_from_settings(settings)
99 # if cmd:
100 # kwargs = {
101 # 'cmd': cmd,
102 # 'timeout': timeout,
103 # }
104 # self.thread = Thread(target=self.run, kwargs=kwargs)
105
106 # def __call__(self, event):
107 # if self.thread is not None:
108 # self.thread.start()
109
110 # def get_command_from_settings(self, settings):
111 # cmd = settings[config_keys.reload_command]
112 # return cmd.split(' ') if cmd else cmd
113
114 # def run(self, cmd, timeout=None):
115 # log.debug('Executing svn proxy reload command %s.', cmd)
116 # try:
117 # output = subprocess32.check_output(
118 # cmd, timeout=timeout, stderr=subprocess32.STDOUT)
119 # log.debug('Svn proxy reload command finished.')
120 # if output:
121 # log.debug('Command output: %s', output)
122 # except subprocess32.TimeoutExpired as e:
123 # log.exception('Timeout while executing svn proxy reload command.')
124 # if e.output:
125 # log.error('Command output: %s', e.output)
126 # except subprocess32.CalledProcessError as e:
127 # log.exception('Error while executing svn proxy reload command.')
128 # if e.output:
129 # log.error('Command output: %s', e.output)
130 # except:
131 # log.exception(
132 # 'Exception while executing svn proxy reload command %s.', cmd)
@@ -1,80 +1,86 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import codecs
22 22 import logging
23 23 import os
24
25 24 from pyramid.renderers import render
26 25
27 from rhodecode.lib.utils import get_rhodecode_realm
26 from rhodecode.events import trigger
27 from rhodecode.lib.utils import get_rhodecode_realm, get_rhodecode_base_path
28 28 from rhodecode.model.db import RepoGroup
29
29 30 from . import config_keys
31 from .events import ModDavSvnConfigChange
30 32
31 33
32 34 log = logging.getLogger(__name__)
33 35
34 36
35 def generate_mod_dav_svn_config(settings, parent_path_root):
37 def generate_mod_dav_svn_config(registry):
36 38 """
37 39 Generate the configuration file for use with subversion's mod_dav_svn
38 40 module. The configuration has to contain a <Location> block for each
39 41 available repository group because the mod_dav_svn module does not support
40 42 repositories organized in sub folders.
41 43 """
44 settings = registry.settings
42 45 config = _render_mod_dav_svn_config(
43 parent_path_root=parent_path_root,
46 parent_path_root=get_rhodecode_base_path(),
44 47 list_parent_path=settings[config_keys.list_parent_path],
45 48 location_root=settings[config_keys.location_root],
46 49 repo_groups=RepoGroup.get_all_repo_groups())
47 50 _write_mod_dav_svn_config(config, settings[config_keys.config_file_path])
48 51
52 # Trigger an event on mod dav svn configuration change.
53 trigger(ModDavSvnConfigChange(), registry)
54
49 55
50 56 def _render_mod_dav_svn_config(
51 57 parent_path_root, list_parent_path, location_root, repo_groups):
52 58 """
53 59 Render mod_dav_svn configuration to string.
54 60 """
55 61 repo_group_paths = []
56 62 for repo_group in repo_groups:
57 63 group_path = repo_group.full_path_splitted
58 64 location = os.path.join(location_root, *group_path)
59 65 parent_path = os.path.join(parent_path_root, *group_path)
60 66 repo_group_paths.append((location, parent_path))
61 67
62 68 context = {
63 69 'location_root': location_root,
64 70 'parent_path_root': parent_path_root,
65 71 'repo_group_paths': repo_group_paths,
66 72 'svn_list_parent_path': list_parent_path,
67 73 'rhodecode_realm': get_rhodecode_realm(),
68 74 }
69 75
70 76 # Render the configuration template to string.
71 77 template = 'rhodecode:svn_support/templates/mod-dav-svn.conf.mako'
72 78 return render(template, context)
73 79
74 80
75 81 def _write_mod_dav_svn_config(config, filepath):
76 82 """
77 83 Write mod_dav_svn config to file.
78 84 """
79 85 with codecs.open(filepath, 'w', encoding='utf-8') as f:
80 86 f.write(config)
General Comments 0
You need to be logged in to leave comments. Login now