Show More
@@ -0,0 +1,43 b'' | |||
|
1 | |RCE| 4.13.1 |RNS| | |
|
2 | ------------------ | |
|
3 | ||
|
4 | Release Date | |
|
5 | ^^^^^^^^^^^^ | |
|
6 | ||
|
7 | - 2018-08-06 | |
|
8 | ||
|
9 | ||
|
10 | New Features | |
|
11 | ^^^^^^^^^^^^ | |
|
12 | ||
|
13 | ||
|
14 | ||
|
15 | General | |
|
16 | ^^^^^^^ | |
|
17 | ||
|
18 | - core: added option to prefix cache keys for usage in cluster. | |
|
19 | - exception-tracker: store event sending exception for easier event fail debugging. | |
|
20 | - maintenance: add repack and fsck for git maintenance execution list. | |
|
21 | ||
|
22 | ||
|
23 | Security | |
|
24 | ^^^^^^^^ | |
|
25 | ||
|
26 | ||
|
27 | ||
|
28 | Performance | |
|
29 | ^^^^^^^^^^^ | |
|
30 | ||
|
31 | ||
|
32 | ||
|
33 | Fixes | |
|
34 | ^^^^^ | |
|
35 | ||
|
36 | - caches: use single default cache dir for all backends. | |
|
37 | - caches: don't use lower in cache settings to support uppercase PATHS | |
|
38 | ||
|
39 | ||
|
40 | Upgrade notes | |
|
41 | ^^^^^^^^^^^^^ | |
|
42 | ||
|
43 | - Unscheduled release addressing reported problems, and improving stability. |
@@ -9,6 +9,7 b' Release Notes' | |||
|
9 | 9 | .. toctree:: |
|
10 | 10 | :maxdepth: 1 |
|
11 | 11 | |
|
12 | release-notes-4.13.1.rst | |
|
12 | 13 | release-notes-4.13.0.rst |
|
13 | 14 | release-notes-4.12.4.rst |
|
14 | 15 | release-notes-4.12.3.rst |
@@ -1653,6 +1653,8 b' self: super: {' | |||
|
1653 | 1653 | self."setuptools-scm" |
|
1654 | 1654 | self."amqp" |
|
1655 | 1655 | self."authomatic" |
|
1656 | self."atomicwrites" | |
|
1657 | self."attrs" | |
|
1656 | 1658 | self."babel" |
|
1657 | 1659 | self."beaker" |
|
1658 | 1660 | self."celery" |
@@ -4,6 +4,8 b' setuptools-scm==2.1.0' | |||
|
4 | 4 | amqp==2.3.1 |
|
5 | 5 | # not released authomatic that has updated some oauth providers |
|
6 | 6 | https://code.rhodecode.com/upstream/authomatic/archive/90a9ce60cc405ae8a2bf5c3713acd5d78579a04e.tar.gz?md5=3c68720a1322b25254009518d1ff6801#egg=authomatic==0.1.0.post1 |
|
7 | atomicwrites==1.1.5 | |
|
8 | attrs==18.1.0 | |
|
7 | 9 | babel==1.3 |
|
8 | 10 | beaker==1.9.1 |
|
9 | 11 | celery==4.1.1 |
@@ -32,8 +32,6 b' log = logging.getLogger(__name__)' | |||
|
32 | 32 | class RepoMaintenanceView(RepoAppView): |
|
33 | 33 | def load_default_context(self): |
|
34 | 34 | c = self._get_local_tmpl_context() |
|
35 | ||
|
36 | ||
|
37 | 35 | return c |
|
38 | 36 | |
|
39 | 37 | @LoginRequired() |
@@ -436,13 +436,23 b' def _sanitize_vcs_settings(settings):' | |||
|
436 | 436 | |
|
437 | 437 | |
|
438 | 438 | def _sanitize_cache_settings(settings): |
|
439 | _string_setting(settings, 'cache_dir', | |
|
440 | os.path.join(tempfile.gettempdir(), 'rc_cache')) | |
|
439 | default_cache_dir = os.path.join(tempfile.gettempdir(), 'rc_cache') | |
|
440 | ||
|
441 | # save default, cache dir, and use it for all backends later. | |
|
442 | default_cache_dir = _string_setting( | |
|
443 | settings, | |
|
444 | 'cache_dir', | |
|
445 | default_cache_dir, lower=False, default_when_empty=True) | |
|
446 | ||
|
447 | # ensure we have our dir created | |
|
448 | if not os.path.isdir(default_cache_dir): | |
|
449 | os.makedirs(default_cache_dir, mode=0755) | |
|
450 | ||
|
441 | 451 | # cache_perms |
|
442 | 452 | _string_setting( |
|
443 | 453 | settings, |
|
444 | 454 | 'rc_cache.cache_perms.backend', |
|
445 | 'dogpile.cache.rc.file_namespace') | |
|
455 | 'dogpile.cache.rc.file_namespace', lower=False) | |
|
446 | 456 | _int_setting( |
|
447 | 457 | settings, |
|
448 | 458 | 'rc_cache.cache_perms.expiration_time', |
@@ -450,13 +460,13 b' def _sanitize_cache_settings(settings):' | |||
|
450 | 460 | _string_setting( |
|
451 | 461 | settings, |
|
452 | 462 | 'rc_cache.cache_perms.arguments.filename', |
|
453 |
os.path.join( |
|
|
463 | os.path.join(default_cache_dir, 'rc_cache_1'), lower=False) | |
|
454 | 464 | |
|
455 | 465 | # cache_repo |
|
456 | 466 | _string_setting( |
|
457 | 467 | settings, |
|
458 | 468 | 'rc_cache.cache_repo.backend', |
|
459 | 'dogpile.cache.rc.file_namespace') | |
|
469 | 'dogpile.cache.rc.file_namespace', lower=False) | |
|
460 | 470 | _int_setting( |
|
461 | 471 | settings, |
|
462 | 472 | 'rc_cache.cache_repo.expiration_time', |
@@ -464,13 +474,13 b' def _sanitize_cache_settings(settings):' | |||
|
464 | 474 | _string_setting( |
|
465 | 475 | settings, |
|
466 | 476 | 'rc_cache.cache_repo.arguments.filename', |
|
467 |
os.path.join( |
|
|
477 | os.path.join(default_cache_dir, 'rc_cache_2'), lower=False) | |
|
468 | 478 | |
|
469 | 479 | # cache_license |
|
470 | 480 | _string_setting( |
|
471 | 481 | settings, |
|
472 | 482 | 'rc_cache.cache_license.backend', |
|
473 | 'dogpile.cache.rc.file_namespace') | |
|
483 | 'dogpile.cache.rc.file_namespace', lower=False) | |
|
474 | 484 | _int_setting( |
|
475 | 485 | settings, |
|
476 | 486 | 'rc_cache.cache_license.expiration_time', |
@@ -478,13 +488,13 b' def _sanitize_cache_settings(settings):' | |||
|
478 | 488 | _string_setting( |
|
479 | 489 | settings, |
|
480 | 490 | 'rc_cache.cache_license.arguments.filename', |
|
481 |
os.path.join( |
|
|
491 | os.path.join(default_cache_dir, 'rc_cache_3'), lower=False) | |
|
482 | 492 | |
|
483 | 493 | # cache_repo_longterm memory, 96H |
|
484 | 494 | _string_setting( |
|
485 | 495 | settings, |
|
486 | 496 | 'rc_cache.cache_repo_longterm.backend', |
|
487 | 'dogpile.cache.rc.memory_lru') | |
|
497 | 'dogpile.cache.rc.memory_lru', lower=False) | |
|
488 | 498 | _int_setting( |
|
489 | 499 | settings, |
|
490 | 500 | 'rc_cache.cache_repo_longterm.expiration_time', |
@@ -498,7 +508,7 b' def _sanitize_cache_settings(settings):' | |||
|
498 | 508 | _string_setting( |
|
499 | 509 | settings, |
|
500 | 510 | 'rc_cache.sql_cache_short.backend', |
|
501 | 'dogpile.cache.rc.memory_lru') | |
|
511 | 'dogpile.cache.rc.memory_lru', lower=False) | |
|
502 | 512 | _int_setting( |
|
503 | 513 | settings, |
|
504 | 514 | 'rc_cache.sql_cache_short.expiration_time', |
@@ -511,6 +521,7 b' def _sanitize_cache_settings(settings):' | |||
|
511 | 521 | |
|
512 | 522 | def _int_setting(settings, name, default): |
|
513 | 523 | settings[name] = int(settings.get(name, default)) |
|
524 | return settings[name] | |
|
514 | 525 | |
|
515 | 526 | |
|
516 | 527 | def _bool_setting(settings, name, default): |
@@ -518,6 +529,7 b' def _bool_setting(settings, name, defaul' | |||
|
518 | 529 | if isinstance(input_val, unicode): |
|
519 | 530 | input_val = input_val.encode('utf8') |
|
520 | 531 | settings[name] = asbool(input_val) |
|
532 | return settings[name] | |
|
521 | 533 | |
|
522 | 534 | |
|
523 | 535 | def _list_setting(settings, name, default): |
@@ -530,13 +542,20 b' def _list_setting(settings, name, defaul' | |||
|
530 | 542 | else: |
|
531 | 543 | # Otherwise we assume it uses pyramids space/newline separation. |
|
532 | 544 | settings[name] = aslist(raw_value) |
|
545 | return settings[name] | |
|
533 | 546 | |
|
534 | 547 | |
|
535 | def _string_setting(settings, name, default, lower=True): | |
|
548 | def _string_setting(settings, name, default, lower=True, default_when_empty=False): | |
|
536 | 549 | value = settings.get(name, default) |
|
550 | ||
|
551 | if default_when_empty and not value: | |
|
552 | # use default value when value is empty | |
|
553 | value = default | |
|
554 | ||
|
537 | 555 | if lower: |
|
538 | 556 | value = value.lower() |
|
539 | 557 | settings[name] = value |
|
558 | return settings[name] | |
|
540 | 559 | |
|
541 | 560 | |
|
542 | 561 | def _substitute_values(mapping, substitutions): |
@@ -81,9 +81,14 b' def get_vcs_server_protocol(config):' | |||
|
81 | 81 | |
|
82 | 82 | |
|
83 | 83 | def set_instance_id(config): |
|
84 | """ Sets a dynamic generated config['instance_id'] if missing or '*' """ | |
|
84 | """ | |
|
85 | Sets a dynamic generated config['instance_id'] if missing or '*' | |
|
86 | E.g instance_id = *cluster-1 or instance_id = * | |
|
87 | """ | |
|
85 | 88 | |
|
86 | 89 | config['instance_id'] = config.get('instance_id') or '' |
|
87 | if config['instance_id'] == '*' or not config['instance_id']: | |
|
90 | instance_id = config['instance_id'] | |
|
91 | if instance_id.startswith('*') or not instance_id: | |
|
92 | prefix = instance_id.lstrip('*') | |
|
88 | 93 | _platform_id = platform.uname()[1] or 'instance' |
|
89 | config['instance_id'] = '%s-%s' % (_platform_id, os.getpid()) | |
|
94 | config['instance_id'] = '%s%s-%s' % (prefix, _platform_id, os.getpid()) |
@@ -17,11 +17,13 b'' | |||
|
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 | import sys | |
|
21 | 21 | import logging |
|
22 | 22 | |
|
23 | 23 | from rhodecode.integrations.registry import IntegrationTypeRegistry |
|
24 | 24 | from rhodecode.integrations.types import webhook, slack, hipchat, email, base |
|
25 | from rhodecode.lib.exc_tracking import store_exception | |
|
26 | ||
|
25 | 27 | log = logging.getLogger(__name__) |
|
26 | 28 | |
|
27 | 29 | |
@@ -61,6 +63,8 b' def integrations_event_handler(event):' | |||
|
61 | 63 | try: |
|
62 | 64 | integration_model.send_event(integration, event) |
|
63 | 65 | except Exception: |
|
66 | exc_info = sys.exc_info() | |
|
67 | store_exception(id(exc_info), exc_info) | |
|
64 | 68 | log.exception( |
|
65 | 69 | 'failure occurred when sending event %s to integration %s' % ( |
|
66 | 70 | event, integration)) |
@@ -87,6 +87,13 b' def _store_exception(exc_id, exc_info, p' | |||
|
87 | 87 | |
|
88 | 88 | |
|
89 | 89 | def store_exception(exc_id, exc_info, prefix=global_prefix): |
|
90 | """ | |
|
91 | Example usage:: | |
|
92 | ||
|
93 | exc_info = sys.exc_info() | |
|
94 | store_exception(id(exc_info), exc_info) | |
|
95 | """ | |
|
96 | ||
|
90 | 97 | try: |
|
91 | 98 | _store_exception(exc_id=exc_id, exc_info=exc_info, prefix=prefix) |
|
92 | 99 | except Exception: |
@@ -51,25 +51,81 b' class GitGC(MaintenanceTask):' | |||
|
51 | 51 | output = [] |
|
52 | 52 | instance = self.db_repo.scm_instance() |
|
53 | 53 | |
|
54 | objects = self._count_objects(instance) | |
|
55 | output.append(objects) | |
|
56 | log.debug('GIT objects:%s', objects) | |
|
57 | ||
|
58 | stdout, stderr = instance.run_git_command( | |
|
59 | ['gc', '--aggressive'], fail_on_stderr=False) | |
|
54 | objects_before = self._count_objects(instance) | |
|
60 | 55 | |
|
61 | out = 'executed git gc --aggressive' | |
|
62 | if stderr: | |
|
63 | out = ''.join(stderr.splitlines()) | |
|
56 | log.debug('GIT objects:%s', objects_before) | |
|
57 | cmd = ['gc', '--aggressive'] | |
|
58 | stdout, stderr = instance.run_git_command(cmd, fail_on_stderr=False) | |
|
64 | 59 | |
|
65 | elif stdout: | |
|
66 | out = ''.join(stdout.splitlines()) | |
|
67 | ||
|
60 | out = 'executed {}'.format(' '.join(cmd)) | |
|
68 | 61 | output.append(out) |
|
69 | 62 | |
|
70 | objects = self._count_objects(instance) | |
|
71 | log.debug('GIT objects:%s', objects) | |
|
72 | output.append(objects) | |
|
63 | out = '' | |
|
64 | if stderr: | |
|
65 | out += ''.join(stderr.splitlines()) | |
|
66 | ||
|
67 | if stdout: | |
|
68 | out += ''.join(stdout.splitlines()) | |
|
69 | ||
|
70 | if out: | |
|
71 | output.append(out) | |
|
72 | ||
|
73 | objects_after = self._count_objects(instance) | |
|
74 | log.debug('GIT objects:%s', objects_after) | |
|
75 | output.append('objects before :' + objects_before) | |
|
76 | output.append('objects after :' + objects_after) | |
|
77 | ||
|
78 | return '\n'.join(output) | |
|
79 | ||
|
80 | ||
|
81 | class GitFSCK(MaintenanceTask): | |
|
82 | human_name = 'GIT FSCK' | |
|
83 | ||
|
84 | def run(self): | |
|
85 | output = [] | |
|
86 | instance = self.db_repo.scm_instance() | |
|
87 | ||
|
88 | cmd = ['fsck', '--full'] | |
|
89 | stdout, stderr = instance.run_git_command(cmd, fail_on_stderr=False) | |
|
90 | ||
|
91 | out = 'executed {}'.format(' '.join(cmd)) | |
|
92 | output.append(out) | |
|
93 | ||
|
94 | out = '' | |
|
95 | if stderr: | |
|
96 | out += ''.join(stderr.splitlines()) | |
|
97 | ||
|
98 | if stdout: | |
|
99 | out += ''.join(stdout.splitlines()) | |
|
100 | ||
|
101 | if out: | |
|
102 | output.append(out) | |
|
103 | ||
|
104 | return '\n'.join(output) | |
|
105 | ||
|
106 | ||
|
107 | class GitRepack(MaintenanceTask): | |
|
108 | human_name = 'GIT Repack' | |
|
109 | ||
|
110 | def run(self): | |
|
111 | output = [] | |
|
112 | instance = self.db_repo.scm_instance() | |
|
113 | cmd = ['repack', '-a', '-d', | |
|
114 | '--window-memory', '10m', '--max-pack-size', '100m'] | |
|
115 | stdout, stderr = instance.run_git_command(cmd, fail_on_stderr=False) | |
|
116 | ||
|
117 | out = 'executed {}'.format(' '.join(cmd)) | |
|
118 | output.append(out) | |
|
119 | out = '' | |
|
120 | ||
|
121 | if stderr: | |
|
122 | out += ''.join(stderr.splitlines()) | |
|
123 | ||
|
124 | if stdout: | |
|
125 | out += ''.join(stdout.splitlines()) | |
|
126 | ||
|
127 | if out: | |
|
128 | output.append(out) | |
|
73 | 129 | |
|
74 | 130 | return '\n'.join(output) |
|
75 | 131 | |
@@ -98,7 +154,7 b' class RepoMaintenance(object):' | |||
|
98 | 154 | """ |
|
99 | 155 | tasks = { |
|
100 | 156 | 'hg': [HGVerify], |
|
101 | 'git': [GitGC], | |
|
157 | 'git': [GitFSCK, GitGC, GitRepack], | |
|
102 | 158 | 'svn': [SVNVerify], |
|
103 | 159 | } |
|
104 | 160 | |
@@ -114,5 +170,6 b' class RepoMaintenance(object):' | |||
|
114 | 170 | def execute(self, db_repo): |
|
115 | 171 | executed_tasks = [] |
|
116 | 172 | for task in self.tasks[db_repo.repo_type]: |
|
117 |
|
|
|
173 | output = task.human_name + ':\n' + task(db_repo).run() + '\n--\n' | |
|
174 | executed_tasks.append(output) | |
|
118 | 175 | return executed_tasks |
General Comments 0
You need to be logged in to leave comments.
Login now