|
|
# Copyright (C) 2010-2024 RhodeCode GmbH
|
|
|
#
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
|
# it under the terms of the GNU Affero General Public License, version 3
|
|
|
# (only), as published by the Free Software Foundation.
|
|
|
#
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
# GNU General Public License for more details.
|
|
|
#
|
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
#
|
|
|
# This program is dual-licensed. If you wish to learn more about the
|
|
|
# RhodeCode Enterprise Edition, including its added features, Support services,
|
|
|
# and proprietary license terms, please see https://rhodecode.com/licenses/
|
|
|
|
|
|
import logging
|
|
|
import redis
|
|
|
|
|
|
from ..lib import rc_cache
|
|
|
from ..lib.ext_json import json
|
|
|
|
|
|
|
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
redis_client = None
|
|
|
|
|
|
|
|
|
class RedisTxnClient:
|
|
|
|
|
|
def __init__(self, url):
|
|
|
self.url = url
|
|
|
self._create_client(url)
|
|
|
|
|
|
def _create_client(self, url):
|
|
|
connection_pool = redis.ConnectionPool.from_url(url)
|
|
|
self.writer_client = redis.StrictRedis(
|
|
|
connection_pool=connection_pool
|
|
|
)
|
|
|
self.reader_client = self.writer_client
|
|
|
|
|
|
def set(self, key, value, expire=24 * 60000):
|
|
|
self.writer_client.set(key, value, ex=expire)
|
|
|
|
|
|
def get(self, key):
|
|
|
return self.reader_client.get(key)
|
|
|
|
|
|
def delete(self, key):
|
|
|
self.writer_client.delete(key)
|
|
|
|
|
|
|
|
|
def get_redis_client(url=''):
|
|
|
|
|
|
global redis_client
|
|
|
if redis_client is not None:
|
|
|
return redis_client
|
|
|
if not url:
|
|
|
from rhodecode import CONFIG
|
|
|
url = CONFIG['vcs.svn.redis_conn']
|
|
|
redis_client = RedisTxnClient(url)
|
|
|
return redis_client
|
|
|
|
|
|
|
|
|
def extract_svn_txn_id(data: bytes):
|
|
|
"""
|
|
|
Helper method for extraction of svn txn_id from submitted XML data during
|
|
|
POST operations
|
|
|
"""
|
|
|
import re
|
|
|
from lxml import etree
|
|
|
|
|
|
try:
|
|
|
root = etree.fromstring(data)
|
|
|
pat = re.compile(r'/txn/(?P<txn_id>.*)')
|
|
|
for el in root:
|
|
|
if el.tag == '{DAV:}source':
|
|
|
for sub_el in el:
|
|
|
if sub_el.tag == '{DAV:}href':
|
|
|
match = pat.search(sub_el.text)
|
|
|
if match:
|
|
|
svn_tx_id = match.groupdict()['txn_id']
|
|
|
return svn_tx_id
|
|
|
except Exception:
|
|
|
log.exception('Failed to extract txn_id')
|
|
|
|
|
|
|
|
|
def get_txn_id_data_key(repo_path, svn_txn_id):
|
|
|
log.debug('svn-txn-id: %s, obtaining data path', svn_txn_id)
|
|
|
repo_key = rc_cache.utils.compute_key_from_params(repo_path)
|
|
|
final_key = f'{repo_key}.{svn_txn_id}.svn_txn_id'
|
|
|
log.debug('computed final key: %s', final_key)
|
|
|
|
|
|
return final_key
|
|
|
|
|
|
|
|
|
def store_txn_id_data(repo_path, svn_txn_id, data_dict):
|
|
|
log.debug('svn-txn-id: %s, storing data', svn_txn_id)
|
|
|
|
|
|
if not svn_txn_id:
|
|
|
log.warning('Cannot store txn_id because it is empty')
|
|
|
return
|
|
|
|
|
|
redis_conn = get_redis_client()
|
|
|
|
|
|
store_key = get_txn_id_data_key(repo_path, svn_txn_id)
|
|
|
store_data = json.dumps(data_dict)
|
|
|
redis_conn.set(store_key, store_data)
|
|
|
|
|
|
|
|
|
def get_txn_id_from_store(repo_path, svn_txn_id, rm_on_read=False):
|
|
|
"""
|
|
|
Reads txn_id from store and if present returns the data for callback manager
|
|
|
"""
|
|
|
log.debug('svn-txn-id: %s, retrieving data', svn_txn_id)
|
|
|
redis_conn = get_redis_client()
|
|
|
|
|
|
store_key = get_txn_id_data_key(repo_path, svn_txn_id)
|
|
|
data = {}
|
|
|
redis_conn.get(store_key)
|
|
|
try:
|
|
|
raw_data = redis_conn.get(store_key)
|
|
|
data = json.loads(raw_data)
|
|
|
except Exception:
|
|
|
log.exception('Failed to get txn_id metadata')
|
|
|
|
|
|
if rm_on_read:
|
|
|
log.debug('Cleaning up txn_id at %s', store_key)
|
|
|
redis_conn.delete(store_key)
|
|
|
|
|
|
return data
|
|
|
|