# bijoe - BI dashboard # Copyright (C) 2015 Entr'ouvert # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # 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 Affero 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 . import hashlib import os from django.utils.six.moves import configparser as ConfigParser from django.utils.six.moves.urllib import parse as urlparse try: import sentry_sdk except ImportError: sentry_sdk = None from django.conf import settings from django.utils.encoding import force_str from hobo.agent.common.management.commands import hobo_deploy from hobo.multitenant.settings_loaders import KnownServices from tenant_schemas.utils import tenant_context def pg_dsn_quote(value): return "'%s'" % str(value).replace('\\', '\\\\').replace('\'', '\\\'') def config_parser_quote(value): return value.replace('%', '%%') def truncate_pg_identifier(identifier, hash_length=6, force_hash=False): if len(identifier) < 64 and not force_hash: return identifier else: # insert hash in the middle, to keep some readability return ( identifier[: (63 - hash_length) // 2] + hashlib.md5(identifier.encode('utf-8')).hexdigest()[:hash_length] + identifier[-(63 - hash_length) // 2 :] ) def schema_from_url(url, hash_length=6): netloc = urlparse.urlparse(url).netloc assert netloc domain = netloc.split(':')[0] pg_identifier = domain.replace('.', '_').replace('-', '_') return truncate_pg_identifier(pg_identifier, hash_length=hash_length) class Command(hobo_deploy.Command): def deploy_specifics(self, hobo_environment, tenant): super().deploy_specifics(hobo_environment, tenant) with tenant_context(tenant): services = hobo_environment.get('services') ini_file = os.path.join(tenant.get_directory(), 'wcs-olap.ini') schemas_path = os.path.join(tenant.get_directory(), 'schemas') config = ConfigParser.SafeConfigParser() config.read(ini_file) self.configure_sentry(config) if not os.path.exists(schemas_path): os.mkdir(schemas_path) if not config.has_section('wcs-olap'): config.add_section('wcs-olap') config.set('wcs-olap', 'cubes_model_dirs', schemas_path) pg_dsn_parts = [] for pg_dsn_part in [ ('NAME', 'dbname'), ('HOST', 'host'), ('USER', 'user'), ('PASSWORD', 'password'), ('PORT', 'port'), ]: if settings.DATABASES['default'].get(pg_dsn_part[0]): pg_dsn_parts.append( '%s=%s' % (pg_dsn_part[1], pg_dsn_quote(settings.DATABASES['default'].get(pg_dsn_part[0]))) ) config.set('wcs-olap', 'pg_dsn', config_parser_quote(' '.join(pg_dsn_parts))) for service in services: if service.get('this'): this = service break else: raise RuntimeError('unable to find this service') our_key = this['secret_key'] for service in services: base_url = service.get('base_url') if ( service.get('this') or not service.get('secret_key') or service['service-id'] != 'wcs' or not service.get('base_url') ): continue elif service.get('secondary') and not config.has_section(base_url): # skip secondary instances unless they were already added, # in that case they need to be kept uptodate continue schema = schema_from_url(base_url) orig = urlparse.urlparse(this.get('base_url')).netloc.split(':')[0] their_key = service.get('secret_key') key = KnownServices.shared_secret(our_key, their_key) if config.has_section(base_url): config.remove_section(base_url) config.add_section(base_url) config.set(base_url, 'orig', orig) config.set(base_url, 'key', key) config.set(base_url, 'schema', schema) config.set(base_url, 'cubes_label', force_str(service.get('title'))) if 'slug' in service: config.set(base_url, 'cubes_slug', service['slug']) with open(ini_file, 'w') as f: config.write(f) def has_sentry(self): if not sentry_sdk: return False if not sentry_sdk.Hub.current or not sentry_sdk.Hub.current.client: # not configured return False client = sentry_sdk.Hub.current.client if not client.dsn: # not configured return False return True def configure_sentry(self, config): if self.has_sentry(): if not config.has_section('sentry'): config.add_section('sentry') config.set('sentry', 'dsn', sentry_sdk.Hub.current.client.dsn) if sentry_sdk.Hub.current.client.options.get('environment'): config.set('sentry', 'environment', sentry_sdk.Hub.current.client.options['environment']) else: if config.has_section('sentry'): config.remove_section('sentry')