"" "
Django settings for blink_crm_api project.
Generated by 'django-admin startproject' using Django 3.1.3.
For more information on this file, see
https://d...content-available-to-author-only...t.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://d...content-available-to-author-only...t.com/en/3.1/ref/settings/
" ""
from pathlib import Path
import os
import socket
import sys
from datetime import timedelta
import dj_database_url
import sentry_sdk
from sentry_sdk.integrations .django import DjangoIntegration
from django.conf import settings
from firebase_admin import initialize_app
from notification.services .slack_service import SlackService
from core import config
import django
from django.utils .translation import gettext
django.utils .translation .ugettext = gettext
# sentry_sdk.init(
# dsn=os.environ.get('SENTRY_DSN', ''),
# integrations=[DjangoIntegration()],
# # Set traces_sample_rate to 1.0 to capture 100%
# # of transactions for performance monitoring.
# # We recommend adjusting this value in production.
# traces_sample_rate=0.1,
# # If you wish to associate users to errors (assuming you are using
# # django.contrib.auth) you may enable sending PII data.
# send_default_pii=True
# )
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path( __file__) .resolve ( ) .parent .parent
# Quick-start development settings - unsuitable for production
# See https://d...content-available-to-author-only...t.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'wnia2ie9-(c2_)4g%ck%bw6lyfjtdgf@imcg*xe*n!uo%1^&0%'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG
= os.
getenv ( 'DEBUG' , False
) == 'True'
ALLOWED_HOSTS = [
'*'
]
print( '_________________THE PLATFORM IS _________' )
print( '_____________' + os.environ .get ( 'PLATFORM' , '' ) + '___________________' )
# Application definition
MIGRATION_COMMAND = sys.argv [ 1 ] in (
'makemigrations' , 'migrate' , 'loaddata' , 'import_coa_blink' , 'import_entry_templates' )
INSTALLED_APPS = [
"django.contrib.admin.apps.SimpleAdminConfig" if MIGRATION_COMMAND else 'django.contrib.admin' ,
'django.contrib.auth' ,
'django.contrib.contenttypes' ,
'django.contrib.sessions' ,
'django.contrib.messages' ,
'django.contrib.postgres' ,
'whitenoise.runserver_nostatic' ,
'django.contrib.staticfiles' ,
'drf_yasg' ,
'rest_framework' ,
'django_reactive' ,
'mathfilters' ,
'crm_app.apps.CrmAppConfig' ,
'ekyc_app.apps.EkycAppConfig' ,
'auth_app.apps.AuthAppConfig' ,
'datasets.apps.DatasetsConfig' ,
'ledger' ,
'lms.apps.LmsConfig' ,
'loan_app.apps.LoanAppConfig' ,
'reports_app.apps.ReportsAppConfig' ,
'payments.apps.PaymentsConfig' ,
"fcm_django" ,
'notification.apps.NotificationConfig' ,
'django_extensions' ,
'polymorphic' ,
'import_export' ,
'django_filters' ,
'rest_framework_simplejwt.token_blacklist' ,
'django_summernote' ,
'rangefilter' ,
'storages' ,
'call_center' ,
'elasticapm.contrib.django' ,
'credit_alerts' ,
'flutter_consumer' ,
'credit_app' ,
'merchant_dashboard' ,
'configurations' ,
'ticket_app' ,
'settlement_dashboard' ,
'tms_app' ,
'newEkyc_app' ,
'call_centerv2' ,
'ticket_appV2' ,
'archive_app' ,
'new_lms' ,
'log_app' ,
'collection' ,
'sales_tracking' ,
'django_rename_app' ,
'django_celery_results' ,
]
MIDDLEWARE = [
'django.middleware.locale.LocaleMiddleware' ,
'django.middleware.security.SecurityMiddleware' ,
'django.contrib.sessions.middleware.SessionMiddleware' ,
'corsheaders.middleware.CorsMiddleware' ,
'django.middleware.common.CommonMiddleware' ,
'django.middleware.csrf.CsrfViewMiddleware' ,
'django.contrib.auth.middleware.AuthenticationMiddleware' ,
'django.contrib.messages.middleware.MessageMiddleware' ,
'django.middleware.clickjacking.XFrameOptionsMiddleware' ,
'whitenoise.middleware.WhiteNoiseMiddleware' ,
# Timezone
"core.timezone_middleware.TimezoneMiddleware" ,
# Translation
"core.translation_middleware.TranslationMiddleware" ,
'elasticapm.contrib.django.middleware.TracingMiddleware'
]
ROOT_URLCONF = 'blink_crm_api.urls'
TEMPLATES = [
{
'BACKEND' : 'django.template.backends.django.DjangoTemplates' ,
'DIRS' : [ os.path .join ( BASE_DIR, 'templates' ) ] ,
'APP_DIRS' : True,
'OPTIONS' : {
'context_processors' : [
'django.template.context_processors.debug' ,
'django.template.context_processors.request' ,
'django.contrib.auth.context_processors.auth' ,
'django.contrib.messages.context_processors.messages' ,
'core.context_processors.from_settings'
] ,
} ,
} ,
]
WSGI_APPLICATION = 'blink_crm_api.wsgi.application'
# Database
# https://d...content-available-to-author-only...t.com/en/3.1/ref/settings/#databases
DATABASES = {
'default' : {
'ENGINE' : 'django.db.backends.sqlite3' ,
'NAME' : BASE_DIR / 'db.sqlite3' ,
}
}
if os.environ .get ( 'DATABASE_URL' ) :
print( 'found env database' )
print( os.environ .get ( 'DATABASE_URL' ) )
DATABASES = { 'default' : dj_database_url.config ( conn_max_age= 600 ) }
if "test" in sys.argv :
DATABASES[ "default" ] = {
"ENGINE" : "django.db.backends.postgresql" ,
"NAME" : "test_db" ,
"USER" : "postgres" ,
"PASSWORD" : "1234" ,
"HOST" : "localhost" ,
"PORT" : "5432" ,
}
AUTH_USER_MODEL = 'auth_app.User'
# Password validation
# https://d...content-available-to-author-only...t.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME' : 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator' ,
} ,
{
'NAME' : 'django.contrib.auth.password_validation.MinimumLengthValidator' ,
} ,
{
'NAME' : 'django.contrib.auth.password_validation.CommonPasswordValidator' ,
} ,
{
'NAME' : 'django.contrib.auth.password_validation.NumericPasswordValidator' ,
} ,
]
# Internationalization
# https://d...content-available-to-author-only...t.com/en/3.1/topics/i18n/
LANGUAGES = [
( 'en' , 'English' ) ,
( 'ar' , 'Arabic' ) ,
]
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'ar'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOCALE_PATHS = (
os.path .join ( BASE_DIR, 'locale/' ) ,
)
# Static files (CSS, JavaScript, Images)
# https://d...content-available-to-author-only...t.com/en/3.1/howto/static-files/
PROJECT_DIR = os.path .dirname ( os.path .abspath ( __file__) )
STATIC_ROOT = os.path .join ( BASE_DIR, 'staticfiles' )
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path .join ( BASE_DIR, 'static' ) ,
]
DATA_UPLOAD_MAX_MEMORY_SIZE= 4194304 #4 MB
CHECK_CONSUMER_RATE
= os.
getenv ( "CHECK_CONSUMER_RATE" , "5/day" )
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES' : (
'rest_framework_simplejwt.authentication.JWTAuthentication' ,
) ,
'EXCEPTION_HANDLER' : 'core.exception_handler.exception_handler' ,
'DEFAULT_THROTTLE_RATES' : {
'check_consumer_daily' : CHECK_CONSUMER_RATE,
}
}
SWAGGER_SETTINGS = {
'SECURITY_DEFINITIONS' : {
'Bearer' : {
'type' : 'apiKey' ,
'name' : 'Authorization' ,
'in' : 'header'
}
}
}
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME' : timedelta( minutes= 30 ) ,
'REFRESH_TOKEN_LIFETIME' : timedelta( days= 5 ) ,
'ROTATE_REFRESH_TOKENS' : True,
'BLACKLIST_AFTER_ROTATION' : True,
'ALGORITHM' : 'HS256' ,
'SIGNING_KEY' : SECRET_KEY,
}
# FOR HTTPS
print( os.environ .get ( 'NOT_SECURE_CONNECTION' ) )
if os.environ .get ( 'NOT_SECURE_CONNECTION' ) :
print( 'Secure connection disabled' )
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False
else :
SECURE_PROXY_SSL_HEADER = ( 'HTTP_X_FORWARDED_PROTO' , 'https' )
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
# Security Headers
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_HSTS_SECONDS = 3600
# CORS
CORS_ALLOW_ALL_ORIGINS = True
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
DEFAULT_FROM_EMAIL = os.environ .get ( 'EMAIL_HOST' , 'tech-alerts@blnk.ai' )
EMAIL_HOST_USER = os.environ .get ( 'EMAIL_HOST' , 'tech-alerts@blnk.ai' )
EMAIL_HOST_PASSWORD = os.environ .get ( 'EMAIL_PASSWORD' , 'jcvl kknv hdze eyvi' )
AWS_ACCESS_KEY_ID = os.environ .get ( 'AWS_ACCESS_KEY_ID' )
AWS_SECRET_ACCESS_KEY = os.environ .get ( 'AWS_SECRET_ACCESS_KEY' )
AWS_STORAGE_BUCKET_NAME = os.environ .get ( 'AWS_STORAGE_BUCKET_NAME' )
AWS_S3_REGION_NAME = os.environ .get ( 'AWS_REGION' )
AWS_S3_SIGNATURE_VERSION = 's3v4'
# SFTP - DJANGO_STORAGE
DEFAULT_FILE_STORAGE = 'storages.backends.sftpstorage.SFTPStorage'
SFTP_STORAGE_HOST = os.environ .get ( 'SFTP_STORAGE_HOST' , '' )
SFTP_STORAGE_ROOT = os.environ .get ( 'SFTP_STORAGE_ROOT' , '' )
#tel3et mohema metlasma
SFTP_STORAGE_PARAMS = {
'username' : os.environ .get ( 'SFTP_STORAGE_USERNAME' , '' ) ,
'password' : os.environ .get ( 'SFTP_STORAGE_PASS' , '' ) ,
'allow_agent' : False,
'look_for_keys' : False,
'timeout' : float ( os.environ .get ( 'SFTP_TIMEOUT' , '10.0' ) ) ,
'banner_timeout' : float ( os.environ .get ( 'SFTP_BANNER_TIMEOUT' , '10.0' ) ) ,
'auth_timeout' : float ( os.environ .get ( 'SFTP_AUTH_TIMEOUT' , '10.0' ) ) ,
}
SFTP_STORAGE_PARAM_BLNK_UPLOADS = {
'username' : os.environ .get ( 'SFTP_STORAGE_BLNK_UPLOADS_USERNAME' , '' ) ,
'password' : os.environ .get ( 'SFTP_STORAGE_BLNK_UPLOADS_PASS' , '' ) ,
'allow_agent' : False,
'look_for_keys' : False,
'timeout' : float ( os.environ .get ( 'SFTP_TIMEOUT' , '10.0' ) ) ,
'banner_timeout' : float ( os.environ .get ( 'SFTP_BANNER_TIMEOUT' , '10.0' ) ) ,
'auth_timeout' : float ( os.environ .get ( 'SFTP_AUTH_TIMEOUT' , '10.0' ) ) ,
}
# Text editor plugin
X_FRAME_OPTIONS = 'SAMEORIGIN'
SUMMERNOTE_CONFIG = {
# You can put custom Summernote settings
'summernote' : {
# Toolbar customization
# https://s...content-available-to-author-only...e.org/deep-dive/#custom-toolbar-popover
'toolbar' : [
[ 'view' , [ 'fullscreen' , 'codeview' ] ] ,
] ,
} ,
'js' : (
'/static/admin/summernote/custom.js' ,
) ,
}
# DATA_UPLOAD_MAX_NUMBER_FIELDS = None
FIREBASE_APP = initialize_app( )
FCM_DJANGO_SETTINGS = {
}
# FCM_DJANGO_SETTINGS = {
# "FCM_SERVER_KEY": os.environ.get('FCM_SERVER_KEY', ''),
# "FCM_SENDER_ID": os.environ.get('FCM_SENDER_ID', ''),
# }
if os.environ .get ( 'USE_X_FORWARDED_HOST' ) :
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ( 'HTTP_X_FORWARDED_PROTO' , 'https' )
# MEDIA_ROOT = "/NationalID/"
#MEDIA_URL = os.environ.get('SFTP_STORAGE_URL', '')
# LOGGING = {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'handlers': {
# 'applogfile': {
# 'level': 'DEBUG',
# 'class': 'logging.FileHandler',
# 'filename': '../blnk_errors.log',
# 'maxBytes': 1024 * 1024 * 15, # 15MB
# 'backupCount': 10,
# },
# },
# 'loggers': {
# 'django': {
# 'handlers': ['applogfile','console'],
# 'level': 'DEBUG',
# },
# }
# }
# LOGGING = {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'handlers': {
# 'applogfile': {
# 'level': 'DEBUG',
# 'class': 'logging.FileHandler',
# 'filename': '../blnk_errors.log',
# 'maxBytes': 1024 * 1024 * 15, # 15MB
# 'backupCount': 10,
# },
# },
# 'loggers': {
# 'django': {
# 'handlers': ['applogfile',],
# 'level': 'DEBUG',
# },
# }
# }
# LOGGING = {
# 'version': 1,
# 'disable_existing_loggers': False,
# 'handlers': {
# 'file': {
# 'level': 'DEBUG',
# 'class': 'logging.FileHandler',
# 'filename': '../blnk_errors.log',
# 'maxBytes': 1024 * 1024 * 15, # 15MB
# 'backupCount': 10,
# },
# },
# 'loggers': {
# 'django': {
# 'handlers': ['file'],
# 'level': 'DEBUG',
# # 'propagate': True,
# },
# },
# }
LOGGING = {
'version' : 1 ,
'disable_existing_loggers' : False,
'formatters' : {
'verbose' : {
'format' : '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d {server_ip} %(message)s'
} ,
} ,
'handlers' : {
'elasticapm' : {
'level' : 'DEBUG' ,
'class' : 'elasticapm.contrib.django.handlers.LoggingHandler' ,
} ,
'console' : {
'level' : 'DEBUG' ,
'class' : 'logging.StreamHandler' ,
'formatter' : 'verbose'
} ,
'to_slack' : {
'level' : 'DEBUG' ,
'formatter' : 'verbose' ,
'class' : 'core.custom_logging.CustomLoggingHandler' ,
'custom_function' : SlackService( os.environ .get ( 'API_ERROR_CHANNEL' ) ) .send_message
}
} ,
'loggers' : {
'mysite' : {
'level' : 'DEBUG' ,
'handlers' : [ 'elasticapm' ] ,
'propagate' : False,
} ,
# Log errors from the Elastic APM module to the console (recommended)
'elasticapm' : {
'level' : 'ERROR' ,
'handlers' : [ 'to_slack' , 'console' ] ,
'propagate' : False,
} ,
'crm_app' : {
'level' : 'DEBUG' ,
'handlers' : [ 'console' ] ,
'propagate' : False,
} ,
} ,
}
# Get the server's IP address
server_ip = socket.gethostbyname ( socket.gethostname ( ) )
LOGGING[ 'formatters' ] [ 'verbose' ] [ 'format' ] = LOGGING[ 'formatters' ] [ 'verbose' ] [ 'format' ] .replace ( '{server_ip}' , server_ip)
print( "APM CLOSE = " , os.environ .get ( 'APM_ClOSE_FLAG' , False) == 'True' )
ELASTIC_APM = {
# Set the required service name. Allowed characters:
# a-z, A-Z, 0-9, -, _, and space
'SERVICE_NAME' : 'django-test' ,
# Set the custom APM Server URL (default: http://localhost:8200)
'SERVER_URL' : os.environ .get ( 'APM_SERVER_URL' , '' ) ,
# Set the service environment
# 'DEBUG': os.environ.get('APM_ClOSE_FLAG',False) == 'True',
'DISABLE_SEND' : os.environ .get ( 'APM_ClOSE_FLAG' , True) == 'True' ,
'CAPTURE_BODY' : 'all' ,
'PROCESSORS' : [
'core.apm_processors.sanitize_processor' ,
'elasticapm.processors.sanitize_stacktrace_locals' ,
'elasticapm.processors.sanitize_http_request_cookies' ,
'elasticapm.processors.sanitize_http_headers' ,
'elasticapm.processors.sanitize_http_wsgi_env' ,
'elasticapm.processors.sanitize_http_request_body' ,
] ,
'INSTRUMENT' : True
}
CELERY_BROKER_URL = f'amqp://{config.RABBITMQ_USERNAME}:{config.RABBITMQ_PASSWORD}@{config.RABBITMQ_HOST}:{config.RABBITMQ_PORT}/{config.RABBITMQ_VIRTUAL_HOST}'
CELERY_RESULT_BACKEND = 'rpc://'
CELERY_ACCEPT_CONTENT = [ 'json' ]
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'UTC'
from urllib.parse import quote_plus
REDIS_HOST
= os.
getenv ( "REDIS_HOST" , "127.0.0.1" ) REDIS_PORT
= os.
getenv ( "REDIS_PORT" , "6379" ) REDIS_PASSWORD_RAW
= os.
getenv ( "REDIS_PASSWORD" , "" ) REDIS_DB
= os.
getenv ( "REDIS_DB" , "1" )
REDIS_PASSWORD = quote_plus( REDIS_PASSWORD_RAW)
REDIS_URL = f"redis://:{REDIS_PASSWORD}@{REDIS_HOST}:{REDIS_PORT}/{REDIS_DB}"
CACHES = {
"default" : {
"BACKEND" : "django_redis.cache.RedisCache" ,
"LOCATION" : REDIS_URL,
"OPTIONS" : {
"CLIENT_CLASS" : "django_redis.client.DefaultClient" ,
} ,
}
}
IiIiCkRqYW5nbyBzZXR0aW5ncyBmb3IgYmxpbmtfY3JtX2FwaSBwcm9qZWN0LgoKR2VuZXJhdGVkIGJ5ICdkamFuZ28tYWRtaW4gc3RhcnRwcm9qZWN0JyB1c2luZyBEamFuZ28gMy4xLjMuCgpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGlzIGZpbGUsIHNlZQpodHRwczovL2QuLi5jb250ZW50LWF2YWlsYWJsZS10by1hdXRob3Itb25seS4uLnQuY29tL2VuLzMuMS90b3BpY3Mvc2V0dGluZ3MvCgpGb3IgdGhlIGZ1bGwgbGlzdCBvZiBzZXR0aW5ncyBhbmQgdGhlaXIgdmFsdWVzLCBzZWUKaHR0cHM6Ly9kLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi50LmNvbS9lbi8zLjEvcmVmL3NldHRpbmdzLwoiIiIKCmZyb20gcGF0aGxpYiBpbXBvcnQgUGF0aAppbXBvcnQgb3MKaW1wb3J0IHNvY2tldAppbXBvcnQgc3lzCmZyb20gZGF0ZXRpbWUgaW1wb3J0IHRpbWVkZWx0YQppbXBvcnQgZGpfZGF0YWJhc2VfdXJsCmltcG9ydCBzZW50cnlfc2RrCmZyb20gc2VudHJ5X3Nkay5pbnRlZ3JhdGlvbnMuZGphbmdvIGltcG9ydCBEamFuZ29JbnRlZ3JhdGlvbgpmcm9tIGRqYW5nby5jb25mIGltcG9ydCBzZXR0aW5ncwpmcm9tIGZpcmViYXNlX2FkbWluIGltcG9ydCBpbml0aWFsaXplX2FwcApmcm9tIG5vdGlmaWNhdGlvbi5zZXJ2aWNlcy5zbGFja19zZXJ2aWNlIGltcG9ydCBTbGFja1NlcnZpY2UKCmZyb20gY29yZSBpbXBvcnQgY29uZmlnCmltcG9ydCBkamFuZ28KZnJvbSBkamFuZ28udXRpbHMudHJhbnNsYXRpb24gaW1wb3J0IGdldHRleHQKZGphbmdvLnV0aWxzLnRyYW5zbGF0aW9uLnVnZXR0ZXh0ID0gZ2V0dGV4dAoKIyBzZW50cnlfc2RrLmluaXQoCiMgICAgIGRzbj1vcy5lbnZpcm9uLmdldCgnU0VOVFJZX0RTTicsICcnKSwKIyAgICAgaW50ZWdyYXRpb25zPVtEamFuZ29JbnRlZ3JhdGlvbigpXSwKCiMgICAgICMgU2V0IHRyYWNlc19zYW1wbGVfcmF0ZSB0byAxLjAgdG8gY2FwdHVyZSAxMDAlCiMgICAgICMgb2YgdHJhbnNhY3Rpb25zIGZvciBwZXJmb3JtYW5jZSBtb25pdG9yaW5nLgojICAgICAjIFdlIHJlY29tbWVuZCBhZGp1c3RpbmcgdGhpcyB2YWx1ZSBpbiBwcm9kdWN0aW9uLgojICAgICB0cmFjZXNfc2FtcGxlX3JhdGU9MC4xLAoKIyAgICAgIyBJZiB5b3Ugd2lzaCB0byBhc3NvY2lhdGUgdXNlcnMgdG8gZXJyb3JzIChhc3N1bWluZyB5b3UgYXJlIHVzaW5nCiMgICAgICMgZGphbmdvLmNvbnRyaWIuYXV0aCkgeW91IG1heSBlbmFibGUgc2VuZGluZyBQSUkgZGF0YS4KIyAgICAgc2VuZF9kZWZhdWx0X3BpaT1UcnVlCiMgKQoKCiMgQnVpbGQgcGF0aHMgaW5zaWRlIHRoZSBwcm9qZWN0IGxpa2UgdGhpczogQkFTRV9ESVIgLyAnc3ViZGlyJy4KQkFTRV9ESVIgPSBQYXRoKF9fZmlsZV9fKS5yZXNvbHZlKCkucGFyZW50LnBhcmVudAoKIyBRdWljay1zdGFydCBkZXZlbG9wbWVudCBzZXR0aW5ncyAtIHVuc3VpdGFibGUgZm9yIHByb2R1Y3Rpb24KIyBTZWUgaHR0cHM6Ly9kLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi50LmNvbS9lbi8zLjEvaG93dG8vZGVwbG95bWVudC9jaGVja2xpc3QvCgojIFNFQ1VSSVRZIFdBUk5JTkc6IGtlZXAgdGhlIHNlY3JldCBrZXkgdXNlZCBpbiBwcm9kdWN0aW9uIHNlY3JldCEKU0VDUkVUX0tFWSA9ICd3bmlhMmllOS0oYzJfKTRnJWNrJWJ3Nmx5Zmp0ZGdmQGltY2cqeGUqbiF1byUxXiYwJScKCiMgU0VDVVJJVFkgV0FSTklORzogZG9uJ3QgcnVuIHdpdGggZGVidWcgdHVybmVkIG9uIGluIHByb2R1Y3Rpb24hCkRFQlVHID0gb3MuZ2V0ZW52KCdERUJVRycsIEZhbHNlKSA9PSAnVHJ1ZScKCkFMTE9XRURfSE9TVFMgPSBbCiAgICAnKicKXQoKCnByaW50KCdfX19fX19fX19fX19fX19fX1RIRSBQTEFURk9STSBJUyBfX19fX19fX18nKQpwcmludCgnX19fX19fX19fX19fXycgKyBvcy5lbnZpcm9uLmdldCgnUExBVEZPUk0nLCAnJykgKyAnX19fX19fX19fX19fX19fX19fXycpCgoKIyBBcHBsaWNhdGlvbiBkZWZpbml0aW9uCk1JR1JBVElPTl9DT01NQU5EID0gc3lzLmFyZ3ZbMV0gaW4gKAogICAgJ21ha2VtaWdyYXRpb25zJywgJ21pZ3JhdGUnLCAnbG9hZGRhdGEnLCAnaW1wb3J0X2NvYV9ibGluaycsICdpbXBvcnRfZW50cnlfdGVtcGxhdGVzJykKSU5TVEFMTEVEX0FQUFMgPSBbCiAgICAiZGphbmdvLmNvbnRyaWIuYWRtaW4uYXBwcy5TaW1wbGVBZG1pbkNvbmZpZyIgaWYgTUlHUkFUSU9OX0NPTU1BTkQgZWxzZSAnZGphbmdvLmNvbnRyaWIuYWRtaW4nLAogICAgJ2RqYW5nby5jb250cmliLmF1dGgnLAogICAgJ2RqYW5nby5jb250cmliLmNvbnRlbnR0eXBlcycsCiAgICAnZGphbmdvLmNvbnRyaWIuc2Vzc2lvbnMnLAogICAgJ2RqYW5nby5jb250cmliLm1lc3NhZ2VzJywKICAgICdkamFuZ28uY29udHJpYi5wb3N0Z3JlcycsCiAgICAnd2hpdGVub2lzZS5ydW5zZXJ2ZXJfbm9zdGF0aWMnLAogICAgJ2RqYW5nby5jb250cmliLnN0YXRpY2ZpbGVzJywKICAgICdkcmZfeWFzZycsCiAgICAncmVzdF9mcmFtZXdvcmsnLAogICAgJ2RqYW5nb19yZWFjdGl2ZScsCiAgICAnbWF0aGZpbHRlcnMnLAogICAgJ2NybV9hcHAuYXBwcy5Dcm1BcHBDb25maWcnLAogICAgJ2VreWNfYXBwLmFwcHMuRWt5Y0FwcENvbmZpZycsCiAgICAnYXV0aF9hcHAuYXBwcy5BdXRoQXBwQ29uZmlnJywKICAgICdkYXRhc2V0cy5hcHBzLkRhdGFzZXRzQ29uZmlnJywKICAgICdsZWRnZXInLAogICAgJ2xtcy5hcHBzLkxtc0NvbmZpZycsCiAgICAnbG9hbl9hcHAuYXBwcy5Mb2FuQXBwQ29uZmlnJywKICAgICdyZXBvcnRzX2FwcC5hcHBzLlJlcG9ydHNBcHBDb25maWcnLAogICAgJ3BheW1lbnRzLmFwcHMuUGF5bWVudHNDb25maWcnLAogICAgImZjbV9kamFuZ28iLAogICAgJ25vdGlmaWNhdGlvbi5hcHBzLk5vdGlmaWNhdGlvbkNvbmZpZycsCiAgICAnZGphbmdvX2V4dGVuc2lvbnMnLAogICAgJ3BvbHltb3JwaGljJywKICAgICdpbXBvcnRfZXhwb3J0JywKICAgICdkamFuZ29fZmlsdGVycycsCiAgICAncmVzdF9mcmFtZXdvcmtfc2ltcGxland0LnRva2VuX2JsYWNrbGlzdCcsCiAgICAnZGphbmdvX3N1bW1lcm5vdGUnLAogICAgJ3JhbmdlZmlsdGVyJywKICAgICdzdG9yYWdlcycsCiAgICAnY2FsbF9jZW50ZXInLAogICAgJ2VsYXN0aWNhcG0uY29udHJpYi5kamFuZ28nLAogICAgJ2NyZWRpdF9hbGVydHMnLAogICAgJ2ZsdXR0ZXJfY29uc3VtZXInLAogICAgJ2NyZWRpdF9hcHAnLAogICAgJ21lcmNoYW50X2Rhc2hib2FyZCcsCiAgICAnY29uZmlndXJhdGlvbnMnLAogICAgJ3RpY2tldF9hcHAnLAogICAgJ3NldHRsZW1lbnRfZGFzaGJvYXJkJywKICAgICd0bXNfYXBwJywKICAgICduZXdFa3ljX2FwcCcsCiAgICAnY2FsbF9jZW50ZXJ2MicsCiAgICAndGlja2V0X2FwcFYyJywKICAgICdhcmNoaXZlX2FwcCcsCiAgICAnbmV3X2xtcycsCiAgICAnbG9nX2FwcCcsCiAgICAnY29sbGVjdGlvbicsCiAgICAnc2FsZXNfdHJhY2tpbmcnLAogICAgJ2RqYW5nb19yZW5hbWVfYXBwJywKICAgICdkamFuZ29fY2VsZXJ5X3Jlc3VsdHMnLAoKXQoKTUlERExFV0FSRSA9IFsKICAgICdkamFuZ28ubWlkZGxld2FyZS5sb2NhbGUuTG9jYWxlTWlkZGxld2FyZScsCiAgICAnZGphbmdvLm1pZGRsZXdhcmUuc2VjdXJpdHkuU2VjdXJpdHlNaWRkbGV3YXJlJywKICAgICdkamFuZ28uY29udHJpYi5zZXNzaW9ucy5taWRkbGV3YXJlLlNlc3Npb25NaWRkbGV3YXJlJywKICAgICdjb3JzaGVhZGVycy5taWRkbGV3YXJlLkNvcnNNaWRkbGV3YXJlJywKICAgICdkamFuZ28ubWlkZGxld2FyZS5jb21tb24uQ29tbW9uTWlkZGxld2FyZScsCiAgICAnZGphbmdvLm1pZGRsZXdhcmUuY3NyZi5Dc3JmVmlld01pZGRsZXdhcmUnLAogICAgJ2RqYW5nby5jb250cmliLmF1dGgubWlkZGxld2FyZS5BdXRoZW50aWNhdGlvbk1pZGRsZXdhcmUnLAogICAgJ2RqYW5nby5jb250cmliLm1lc3NhZ2VzLm1pZGRsZXdhcmUuTWVzc2FnZU1pZGRsZXdhcmUnLAogICAgJ2RqYW5nby5taWRkbGV3YXJlLmNsaWNramFja2luZy5YRnJhbWVPcHRpb25zTWlkZGxld2FyZScsCiAgICAnd2hpdGVub2lzZS5taWRkbGV3YXJlLldoaXRlTm9pc2VNaWRkbGV3YXJlJywKICAgICMgVGltZXpvbmUKICAgICJjb3JlLnRpbWV6b25lX21pZGRsZXdhcmUuVGltZXpvbmVNaWRkbGV3YXJlIiwKICAgICMgVHJhbnNsYXRpb24KICAgICJjb3JlLnRyYW5zbGF0aW9uX21pZGRsZXdhcmUuVHJhbnNsYXRpb25NaWRkbGV3YXJlIiwKICAgICdlbGFzdGljYXBtLmNvbnRyaWIuZGphbmdvLm1pZGRsZXdhcmUuVHJhY2luZ01pZGRsZXdhcmUnCl0KCgpST09UX1VSTENPTkYgPSAnYmxpbmtfY3JtX2FwaS51cmxzJwoKVEVNUExBVEVTID0gWwogICAgewogICAgICAgICdCQUNLRU5EJzogJ2RqYW5nby50ZW1wbGF0ZS5iYWNrZW5kcy5kamFuZ28uRGphbmdvVGVtcGxhdGVzJywKICAgICAgICAnRElSUyc6IFtvcy5wYXRoLmpvaW4oQkFTRV9ESVIsICd0ZW1wbGF0ZXMnKV0sCiAgICAgICAgJ0FQUF9ESVJTJzogVHJ1ZSwKICAgICAgICAnT1BUSU9OUyc6IHsKICAgICAgICAgICAgJ2NvbnRleHRfcHJvY2Vzc29ycyc6IFsKICAgICAgICAgICAgICAgICdkamFuZ28udGVtcGxhdGUuY29udGV4dF9wcm9jZXNzb3JzLmRlYnVnJywKICAgICAgICAgICAgICAgICdkamFuZ28udGVtcGxhdGUuY29udGV4dF9wcm9jZXNzb3JzLnJlcXVlc3QnLAogICAgICAgICAgICAgICAgJ2RqYW5nby5jb250cmliLmF1dGguY29udGV4dF9wcm9jZXNzb3JzLmF1dGgnLAogICAgICAgICAgICAgICAgJ2RqYW5nby5jb250cmliLm1lc3NhZ2VzLmNvbnRleHRfcHJvY2Vzc29ycy5tZXNzYWdlcycsCiAgICAgICAgICAgICAgICAnY29yZS5jb250ZXh0X3Byb2Nlc3NvcnMuZnJvbV9zZXR0aW5ncycKICAgICAgICAgICAgXSwKICAgICAgICB9LAogICAgfSwKXQoKV1NHSV9BUFBMSUNBVElPTiA9ICdibGlua19jcm1fYXBpLndzZ2kuYXBwbGljYXRpb24nCgojIERhdGFiYXNlCiMgaHR0cHM6Ly9kLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi50LmNvbS9lbi8zLjEvcmVmL3NldHRpbmdzLyNkYXRhYmFzZXMKCkRBVEFCQVNFUyA9IHsKICAgICdkZWZhdWx0JzogewogICAgICAgICdFTkdJTkUnOiAnZGphbmdvLmRiLmJhY2tlbmRzLnNxbGl0ZTMnLAogICAgICAgICdOQU1FJzogQkFTRV9ESVIgLyAnZGIuc3FsaXRlMycsCiAgICB9Cn0KCmlmIG9zLmVudmlyb24uZ2V0KCdEQVRBQkFTRV9VUkwnKToKICAgIHByaW50KCdmb3VuZCBlbnYgZGF0YWJhc2UnKQogICAgcHJpbnQob3MuZW52aXJvbi5nZXQoJ0RBVEFCQVNFX1VSTCcpKQogICAgREFUQUJBU0VTID0geydkZWZhdWx0JzogZGpfZGF0YWJhc2VfdXJsLmNvbmZpZyhjb25uX21heF9hZ2U9NjAwKX0KCmlmICJ0ZXN0IiBpbiBzeXMuYXJndjoKICAgIERBVEFCQVNFU1siZGVmYXVsdCJdID0gewogICAgICAgICJFTkdJTkUiOiAiZGphbmdvLmRiLmJhY2tlbmRzLnBvc3RncmVzcWwiLAogICAgICAgICJOQU1FIjogInRlc3RfZGIiLAogICAgICAgICJVU0VSIjogInBvc3RncmVzIiwKICAgICAgICAiUEFTU1dPUkQiOiAiMTIzNCIsCiAgICAgICAgIkhPU1QiOiAibG9jYWxob3N0IiwKICAgICAgICAiUE9SVCI6ICI1NDMyIiwKICAgIH0KCkFVVEhfVVNFUl9NT0RFTCA9ICdhdXRoX2FwcC5Vc2VyJwoKIyBQYXNzd29yZCB2YWxpZGF0aW9uCiMgaHR0cHM6Ly9kLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi50LmNvbS9lbi8zLjEvcmVmL3NldHRpbmdzLyNhdXRoLXBhc3N3b3JkLXZhbGlkYXRvcnMKCkFVVEhfUEFTU1dPUkRfVkFMSURBVE9SUyA9IFsKICAgIHsKICAgICAgICAnTkFNRSc6ICdkamFuZ28uY29udHJpYi5hdXRoLnBhc3N3b3JkX3ZhbGlkYXRpb24uVXNlckF0dHJpYnV0ZVNpbWlsYXJpdHlWYWxpZGF0b3InLAogICAgfSwKICAgIHsKICAgICAgICAnTkFNRSc6ICdkamFuZ28uY29udHJpYi5hdXRoLnBhc3N3b3JkX3ZhbGlkYXRpb24uTWluaW11bUxlbmd0aFZhbGlkYXRvcicsCiAgICB9LAogICAgewogICAgICAgICdOQU1FJzogJ2RqYW5nby5jb250cmliLmF1dGgucGFzc3dvcmRfdmFsaWRhdGlvbi5Db21tb25QYXNzd29yZFZhbGlkYXRvcicsCiAgICB9LAogICAgewogICAgICAgICdOQU1FJzogJ2RqYW5nby5jb250cmliLmF1dGgucGFzc3dvcmRfdmFsaWRhdGlvbi5OdW1lcmljUGFzc3dvcmRWYWxpZGF0b3InLAogICAgfSwKXQoKIyBJbnRlcm5hdGlvbmFsaXphdGlvbgojIGh0dHBzOi8vZC4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4udC5jb20vZW4vMy4xL3RvcGljcy9pMThuLwoKTEFOR1VBR0VTID0gWwogICAgKCdlbicsICdFbmdsaXNoJyksCiAgICAoJ2FyJywgJ0FyYWJpYycpLApdCgoKIyBMQU5HVUFHRV9DT0RFID0gJ2VuLXVzJwoKTEFOR1VBR0VfQ09ERSA9ICdhcicKVElNRV9aT05FID0gJ1VUQycKClVTRV9JMThOID0gVHJ1ZQoKVVNFX0wxME4gPSBUcnVlCgpVU0VfVFogPSBUcnVlCgpMT0NBTEVfUEFUSFMgPSAoCiAgICBvcy5wYXRoLmpvaW4oQkFTRV9ESVIsICdsb2NhbGUvJyksCikKCgojIFN0YXRpYyBmaWxlcyAoQ1NTLCBKYXZhU2NyaXB0LCBJbWFnZXMpCiMgaHR0cHM6Ly9kLi4uY29udGVudC1hdmFpbGFibGUtdG8tYXV0aG9yLW9ubHkuLi50LmNvbS9lbi8zLjEvaG93dG8vc3RhdGljLWZpbGVzLwoKUFJPSkVDVF9ESVIgPSBvcy5wYXRoLmRpcm5hbWUob3MucGF0aC5hYnNwYXRoKF9fZmlsZV9fKSkKClNUQVRJQ19ST09UID0gb3MucGF0aC5qb2luKEJBU0VfRElSLCAnc3RhdGljZmlsZXMnKQpTVEFUSUNfVVJMID0gJy9zdGF0aWMvJwpTVEFUSUNGSUxFU19ESVJTID0gWwogICAgb3MucGF0aC5qb2luKEJBU0VfRElSLCAnc3RhdGljJyksCl0KREFUQV9VUExPQURfTUFYX01FTU9SWV9TSVpFPTQxOTQzMDQgIzQgTUIKCkNIRUNLX0NPTlNVTUVSX1JBVEUgPSBvcy5nZXRlbnYoIkNIRUNLX0NPTlNVTUVSX1JBVEUiLCAiNS9kYXkiKQoKUkVTVF9GUkFNRVdPUksgPSB7CiAgICAnREVGQVVMVF9BVVRIRU5USUNBVElPTl9DTEFTU0VTJzogKAogICAgICAgICdyZXN0X2ZyYW1ld29ya19zaW1wbGVqd3QuYXV0aGVudGljYXRpb24uSldUQXV0aGVudGljYXRpb24nLAogICAgKSwKICAgICdFWENFUFRJT05fSEFORExFUic6ICdjb3JlLmV4Y2VwdGlvbl9oYW5kbGVyLmV4Y2VwdGlvbl9oYW5kbGVyJywKICAgICdERUZBVUxUX1RIUk9UVExFX1JBVEVTJzogewogICAgICAgICdjaGVja19jb25zdW1lcl9kYWlseSc6IENIRUNLX0NPTlNVTUVSX1JBVEUsCiAgICB9Cn0KClNXQUdHRVJfU0VUVElOR1MgPSB7CiAgICAnU0VDVVJJVFlfREVGSU5JVElPTlMnOiB7CiAgICAgICAgJ0JlYXJlcic6IHsKICAgICAgICAgICAgJ3R5cGUnOiAnYXBpS2V5JywKICAgICAgICAgICAgJ25hbWUnOiAnQXV0aG9yaXphdGlvbicsCiAgICAgICAgICAgICdpbic6ICdoZWFkZXInCiAgICAgICAgfQogICAgfQp9CgpTSU1QTEVfSldUID0gewogICAgJ0FDQ0VTU19UT0tFTl9MSUZFVElNRSc6IHRpbWVkZWx0YShtaW51dGVzPTMwKSwKICAgICdSRUZSRVNIX1RPS0VOX0xJRkVUSU1FJzogdGltZWRlbHRhKGRheXM9NSksCiAgICAnUk9UQVRFX1JFRlJFU0hfVE9LRU5TJzogVHJ1ZSwKICAgICdCTEFDS0xJU1RfQUZURVJfUk9UQVRJT04nOiBUcnVlLAogICAgJ0FMR09SSVRITSc6ICdIUzI1NicsCiAgICAnU0lHTklOR19LRVknOiBTRUNSRVRfS0VZLAp9CgojIEZPUiBIVFRQUwpwcmludChvcy5lbnZpcm9uLmdldCgnTk9UX1NFQ1VSRV9DT05ORUNUSU9OJykpCmlmIG9zLmVudmlyb24uZ2V0KCdOT1RfU0VDVVJFX0NPTk5FQ1RJT04nKToKICAgIHByaW50KCdTZWN1cmUgY29ubmVjdGlvbiBkaXNhYmxlZCcpCiAgICBTRVNTSU9OX0NPT0tJRV9TRUNVUkUgPSBGYWxzZQogICAgQ1NSRl9DT09LSUVfU0VDVVJFID0gRmFsc2UKZWxzZToKICAgIFNFQ1VSRV9QUk9YWV9TU0xfSEVBREVSID0gKCdIVFRQX1hfRk9SV0FSREVEX1BST1RPJywgJ2h0dHBzJykKICAgIFNFU1NJT05fQ09PS0lFX1NFQ1VSRSA9IFRydWUKICAgIENTUkZfQ09PS0lFX1NFQ1VSRSA9IFRydWUKICAgIFNFQ1VSRV9TU0xfUkVESVJFQ1QgPSBUcnVlCgogICAgIyBTZWN1cml0eSBIZWFkZXJzCiAgICBTRUNVUkVfQ09OVEVOVF9UWVBFX05PU05JRkYgPSBUcnVlCiAgICBTRUNVUkVfSFNUU19JTkNMVURFX1NVQkRPTUFJTlMgPSBUcnVlCiAgICBTRUNVUkVfSFNUU19QUkVMT0FEID0gVHJ1ZQogICAgU0VDVVJFX0hTVFNfU0VDT05EUyA9IDM2MDAKCiMgQ09SUwpDT1JTX0FMTE9XX0FMTF9PUklHSU5TID0gVHJ1ZQoKRU1BSUxfQkFDS0VORCA9ICdkamFuZ28uY29yZS5tYWlsLmJhY2tlbmRzLnNtdHAuRW1haWxCYWNrZW5kJwpFTUFJTF9IT1NUID0gJ3NtdHAuZ21haWwuY29tJwpFTUFJTF9VU0VfVExTID0gVHJ1ZQpFTUFJTF9QT1JUID0gNTg3CkRFRkFVTFRfRlJPTV9FTUFJTCA9IG9zLmVudmlyb24uZ2V0KCdFTUFJTF9IT1NUJywndGVjaC1hbGVydHNAYmxuay5haScpCkVNQUlMX0hPU1RfVVNFUiA9IG9zLmVudmlyb24uZ2V0KCdFTUFJTF9IT1NUJywndGVjaC1hbGVydHNAYmxuay5haScpCkVNQUlMX0hPU1RfUEFTU1dPUkQgPSBvcy5lbnZpcm9uLmdldCgnRU1BSUxfUEFTU1dPUkQnLCdqY3ZsIGtrbnYgaGR6ZSBleXZpJykKCgpBV1NfQUNDRVNTX0tFWV9JRCA9IG9zLmVudmlyb24uZ2V0KCdBV1NfQUNDRVNTX0tFWV9JRCcpCkFXU19TRUNSRVRfQUNDRVNTX0tFWSA9IG9zLmVudmlyb24uZ2V0KCdBV1NfU0VDUkVUX0FDQ0VTU19LRVknKQpBV1NfU1RPUkFHRV9CVUNLRVRfTkFNRSA9IG9zLmVudmlyb24uZ2V0KCdBV1NfU1RPUkFHRV9CVUNLRVRfTkFNRScpCgpBV1NfUzNfUkVHSU9OX05BTUUgPSBvcy5lbnZpcm9uLmdldCgnQVdTX1JFR0lPTicpCkFXU19TM19TSUdOQVRVUkVfVkVSU0lPTiA9ICdzM3Y0JwoKIyBTRlRQIC0gREpBTkdPX1NUT1JBR0UKREVGQVVMVF9GSUxFX1NUT1JBR0UgPSAnc3RvcmFnZXMuYmFja2VuZHMuc2Z0cHN0b3JhZ2UuU0ZUUFN0b3JhZ2UnClNGVFBfU1RPUkFHRV9IT1NUID0gb3MuZW52aXJvbi5nZXQoJ1NGVFBfU1RPUkFHRV9IT1NUJywgJycpClNGVFBfU1RPUkFHRV9ST09UID0gb3MuZW52aXJvbi5nZXQoJ1NGVFBfU1RPUkFHRV9ST09UJywgJycpCiN0ZWwzZXQgbW9oZW1hIG1ldGxhc21hClNGVFBfU1RPUkFHRV9QQVJBTVMgPSB7CiAgICAndXNlcm5hbWUnOiBvcy5lbnZpcm9uLmdldCgnU0ZUUF9TVE9SQUdFX1VTRVJOQU1FJywgJycpLAogICAgJ3Bhc3N3b3JkJzogb3MuZW52aXJvbi5nZXQoJ1NGVFBfU1RPUkFHRV9QQVNTJywgJycpLAogICAgJ2FsbG93X2FnZW50JzogRmFsc2UsCiAgICAnbG9va19mb3Jfa2V5cyc6IEZhbHNlLAogICAgJ3RpbWVvdXQnOiBmbG9hdChvcy5lbnZpcm9uLmdldCgnU0ZUUF9USU1FT1VUJywgJzEwLjAnKSksCiAgICAnYmFubmVyX3RpbWVvdXQnOiBmbG9hdChvcy5lbnZpcm9uLmdldCgnU0ZUUF9CQU5ORVJfVElNRU9VVCcsICcxMC4wJykpLAogICAgJ2F1dGhfdGltZW91dCc6IGZsb2F0KG9zLmVudmlyb24uZ2V0KCdTRlRQX0FVVEhfVElNRU9VVCcsICcxMC4wJykpLAp9CgpTRlRQX1NUT1JBR0VfUEFSQU1fQkxOS19VUExPQURTID0gewogICAgJ3VzZXJuYW1lJzogb3MuZW52aXJvbi5nZXQoJ1NGVFBfU1RPUkFHRV9CTE5LX1VQTE9BRFNfVVNFUk5BTUUnLCAnJyksCiAgICAncGFzc3dvcmQnOiBvcy5lbnZpcm9uLmdldCgnU0ZUUF9TVE9SQUdFX0JMTktfVVBMT0FEU19QQVNTJywgJycpLAogICAgJ2FsbG93X2FnZW50JzogRmFsc2UsCiAgICAnbG9va19mb3Jfa2V5cyc6IEZhbHNlLAogICAgJ3RpbWVvdXQnOiBmbG9hdChvcy5lbnZpcm9uLmdldCgnU0ZUUF9USU1FT1VUJywgJzEwLjAnKSksCiAgICAnYmFubmVyX3RpbWVvdXQnOiBmbG9hdChvcy5lbnZpcm9uLmdldCgnU0ZUUF9CQU5ORVJfVElNRU9VVCcsICcxMC4wJykpLAogICAgJ2F1dGhfdGltZW91dCc6IGZsb2F0KG9zLmVudmlyb24uZ2V0KCdTRlRQX0FVVEhfVElNRU9VVCcsICcxMC4wJykpLAp9CgoKIyBUZXh0IGVkaXRvciBwbHVnaW4KWF9GUkFNRV9PUFRJT05TID0gJ1NBTUVPUklHSU4nClNVTU1FUk5PVEVfQ09ORklHID0gewogICAgIyBZb3UgY2FuIHB1dCBjdXN0b20gU3VtbWVybm90ZSBzZXR0aW5ncwogICAgJ3N1bW1lcm5vdGUnOiB7CiAgICAgICAgIyBUb29sYmFyIGN1c3RvbWl6YXRpb24KICAgICAgICAjIGh0dHBzOi8vcy4uLmNvbnRlbnQtYXZhaWxhYmxlLXRvLWF1dGhvci1vbmx5Li4uZS5vcmcvZGVlcC1kaXZlLyNjdXN0b20tdG9vbGJhci1wb3BvdmVyCiAgICAgICAgJ3Rvb2xiYXInOiBbCiAgICAgICAgICAgIFsndmlldycsIFsnZnVsbHNjcmVlbicsICdjb2RldmlldyddXSwKICAgICAgICBdLAogICAgfSwKICAgICdqcyc6ICgKICAgICAgICAnL3N0YXRpYy9hZG1pbi9zdW1tZXJub3RlL2N1c3RvbS5qcycsCiAgICApLAp9CgojIERBVEFfVVBMT0FEX01BWF9OVU1CRVJfRklFTERTID0gTm9uZQoKRklSRUJBU0VfQVBQID0gaW5pdGlhbGl6ZV9hcHAoKQoKRkNNX0RKQU5HT19TRVRUSU5HUyA9IHsKCn0KIyBGQ01fREpBTkdPX1NFVFRJTkdTID0gewojICAgICAiRkNNX1NFUlZFUl9LRVkiOiBvcy5lbnZpcm9uLmdldCgnRkNNX1NFUlZFUl9LRVknLCAnJyksCiMgICAgICJGQ01fU0VOREVSX0lEIjogb3MuZW52aXJvbi5nZXQoJ0ZDTV9TRU5ERVJfSUQnLCAnJyksCiMgfQoKCmlmIG9zLmVudmlyb24uZ2V0KCdVU0VfWF9GT1JXQVJERURfSE9TVCcpOgogICAgVVNFX1hfRk9SV0FSREVEX0hPU1QgPSBUcnVlCiAgICBTRUNVUkVfUFJPWFlfU1NMX0hFQURFUiA9ICgnSFRUUF9YX0ZPUldBUkRFRF9QUk9UTycsICdodHRwcycpCgojIE1FRElBX1JPT1QgPSAiL05hdGlvbmFsSUQvIgojTUVESUFfVVJMID0gb3MuZW52aXJvbi5nZXQoJ1NGVFBfU1RPUkFHRV9VUkwnLCAnJykKCiMgTE9HR0lORyA9IHsKIyAgICAgJ3ZlcnNpb24nOiAxLAojICAgICAnZGlzYWJsZV9leGlzdGluZ19sb2dnZXJzJzogRmFsc2UsCiMgICAgICdoYW5kbGVycyc6IHsKIyAgICAgICAgICdhcHBsb2dmaWxlJzogewojICAgICAgICAgICAgICdsZXZlbCc6ICdERUJVRycsCiMgICAgICAgICAgICAgJ2NsYXNzJzogJ2xvZ2dpbmcuRmlsZUhhbmRsZXInLAojICAgICAgICAgICAgICdmaWxlbmFtZSc6ICcuLi9ibG5rX2Vycm9ycy5sb2cnLAojICAgICAgICAgICAgICdtYXhCeXRlcyc6IDEwMjQgKiAxMDI0ICogMTUsICAjIDE1TUIKIyAgICAgICAgICAgICAnYmFja3VwQ291bnQnOiAxMCwKIyAgICAgICAgIH0sCiMgICAgIH0sCiMgICAgICdsb2dnZXJzJzogewojICAgICAgICAgICAgICdkamFuZ28nOiB7CiMgICAgICAgICAgICAgICAgICdoYW5kbGVycyc6IFsnYXBwbG9nZmlsZScsJ2NvbnNvbGUnXSwKIyAgICAgICAgICAgICAgICAgJ2xldmVsJzogJ0RFQlVHJywKIyAgICAgICAgICAgICB9LAojICAgICAgICAgfQojIH0KCiMgTE9HR0lORyA9IHsKIyAgICAgJ3ZlcnNpb24nOiAxLAojICAgICAnZGlzYWJsZV9leGlzdGluZ19sb2dnZXJzJzogRmFsc2UsCiMgICAgICdoYW5kbGVycyc6IHsKIyAgICAgICAgICdhcHBsb2dmaWxlJzogewojICAgICAgICAgICAgICdsZXZlbCc6ICdERUJVRycsCiMgICAgICAgICAgICAgJ2NsYXNzJzogJ2xvZ2dpbmcuRmlsZUhhbmRsZXInLAojICAgICAgICAgICAgICdmaWxlbmFtZSc6ICcuLi9ibG5rX2Vycm9ycy5sb2cnLAojICAgICAgICAgICAgICdtYXhCeXRlcyc6IDEwMjQgKiAxMDI0ICogMTUsICAjIDE1TUIKIyAgICAgICAgICAgICAnYmFja3VwQ291bnQnOiAxMCwKIyAgICAgICAgIH0sCiMgICAgIH0sCiMgICAgICdsb2dnZXJzJzogewojICAgICAgICAgICAgICdkamFuZ28nOiB7CiMgICAgICAgICAgICAgICAgICdoYW5kbGVycyc6IFsnYXBwbG9nZmlsZScsXSwKIyAgICAgICAgICAgICAgICAgJ2xldmVsJzogJ0RFQlVHJywKIyAgICAgICAgICAgICB9LAojICAgICAgICAgfQojIH0KCgojIExPR0dJTkcgPSB7CiMgICAgICd2ZXJzaW9uJzogMSwKIyAgICAgJ2Rpc2FibGVfZXhpc3RpbmdfbG9nZ2Vycyc6IEZhbHNlLAojICAgICAnaGFuZGxlcnMnOiB7CiMgICAgICAgICAnZmlsZSc6IHsKIyAgICAgICAgICAgICAnbGV2ZWwnOiAnREVCVUcnLAojICAgICAgICAgICAgICdjbGFzcyc6ICdsb2dnaW5nLkZpbGVIYW5kbGVyJywKIyAgICAgICAgICAgICAnZmlsZW5hbWUnOiAnLi4vYmxua19lcnJvcnMubG9nJywKIyAgICAgICAgICAgICAnbWF4Qnl0ZXMnOiAxMDI0ICogMTAyNCAqIDE1LCAgIyAxNU1CCiMgICAgICAgICAgICAgJ2JhY2t1cENvdW50JzogMTAsCiMgICAgICAgICB9LAojICAgICB9LAojICAgICAnbG9nZ2Vycyc6IHsKIyAgICAgICAgICdkamFuZ28nOiB7CiMgICAgICAgICAgICAgJ2hhbmRsZXJzJzogWydmaWxlJ10sCiMgICAgICAgICAgICAgJ2xldmVsJzogJ0RFQlVHJywKIyAgICAgICAgICAgICAjICdwcm9wYWdhdGUnOiBUcnVlLAojICAgICAgICAgfSwKIyAgICAgfSwKIyB9CgpMT0dHSU5HID0gewogICAgJ3ZlcnNpb24nOiAxLAogICAgJ2Rpc2FibGVfZXhpc3RpbmdfbG9nZ2Vycyc6IEZhbHNlLAogICAgJ2Zvcm1hdHRlcnMnOiB7CiAgICAgICAgJ3ZlcmJvc2UnOiB7CiAgICAgICAgICAgICdmb3JtYXQnOiAnJShsZXZlbG5hbWUpcyAlKGFzY3RpbWUpcyAlKG1vZHVsZSlzICUocHJvY2VzcylkICUodGhyZWFkKWQge3NlcnZlcl9pcH0gJShtZXNzYWdlKXMnCiAgICAgICAgfSwKICAgIH0sCiAgICAnaGFuZGxlcnMnOiB7CiAgICAgICAgJ2VsYXN0aWNhcG0nOiB7CiAgICAgICAgICAgICdsZXZlbCc6ICdERUJVRycsCiAgICAgICAgICAgICdjbGFzcyc6ICdlbGFzdGljYXBtLmNvbnRyaWIuZGphbmdvLmhhbmRsZXJzLkxvZ2dpbmdIYW5kbGVyJywKICAgICAgICB9LAogICAgICAgICdjb25zb2xlJzogewogICAgICAgICAgICAnbGV2ZWwnOiAnREVCVUcnLAogICAgICAgICAgICAnY2xhc3MnOiAnbG9nZ2luZy5TdHJlYW1IYW5kbGVyJywKICAgICAgICAgICAgJ2Zvcm1hdHRlcic6ICd2ZXJib3NlJwogICAgICAgIH0sCiAgICAgICAgJ3RvX3NsYWNrJzogewogICAgICAgICAgICAnbGV2ZWwnOiAnREVCVUcnLAogICAgICAgICAgICAnZm9ybWF0dGVyJzogJ3ZlcmJvc2UnLAogICAgICAgICAgICAnY2xhc3MnOiAnY29yZS5jdXN0b21fbG9nZ2luZy5DdXN0b21Mb2dnaW5nSGFuZGxlcicsCiAgICAgICAgICAgICdjdXN0b21fZnVuY3Rpb24nOiBTbGFja1NlcnZpY2Uob3MuZW52aXJvbi5nZXQoJ0FQSV9FUlJPUl9DSEFOTkVMJykpLnNlbmRfbWVzc2FnZQogICAgICAgIH0KICAgIH0sCiAgICAnbG9nZ2Vycyc6IHsKICAgICAgICAnbXlzaXRlJzogewogICAgICAgICAgICAnbGV2ZWwnOiAnREVCVUcnLAogICAgICAgICAgICAnaGFuZGxlcnMnOiBbJ2VsYXN0aWNhcG0nXSwKICAgICAgICAgICAgJ3Byb3BhZ2F0ZSc6IEZhbHNlLAogICAgICAgIH0sCiAgICAgICAgIyBMb2cgZXJyb3JzIGZyb20gdGhlIEVsYXN0aWMgQVBNIG1vZHVsZSB0byB0aGUgY29uc29sZSAocmVjb21tZW5kZWQpCiAgICAgICAgJ2VsYXN0aWNhcG0nOiB7CiAgICAgICAgICAgICdsZXZlbCc6ICdFUlJPUicsCiAgICAgICAgICAgICdoYW5kbGVycyc6IFsndG9fc2xhY2snLCAnY29uc29sZSddLAogICAgICAgICAgICAncHJvcGFnYXRlJzogRmFsc2UsCiAgICAgICAgfSwKICAgICAgICAnY3JtX2FwcCc6IHsKICAgICAgICAgICAgJ2xldmVsJzogJ0RFQlVHJywKICAgICAgICAgICAgJ2hhbmRsZXJzJzogWyAnY29uc29sZSddLAogICAgICAgICAgICAncHJvcGFnYXRlJzogRmFsc2UsCiAgICAgICAgfSwKICAgIH0sCn0KCiMgR2V0IHRoZSBzZXJ2ZXIncyBJUCBhZGRyZXNzCnNlcnZlcl9pcCA9IHNvY2tldC5nZXRob3N0YnluYW1lKHNvY2tldC5nZXRob3N0bmFtZSgpKQpMT0dHSU5HWydmb3JtYXR0ZXJzJ11bJ3ZlcmJvc2UnXVsnZm9ybWF0J10gPSBMT0dHSU5HWydmb3JtYXR0ZXJzJ11bJ3ZlcmJvc2UnXVsnZm9ybWF0J10ucmVwbGFjZSgne3NlcnZlcl9pcH0nLCBzZXJ2ZXJfaXApCgpwcmludCgiQVBNIENMT1NFID0gIixvcy5lbnZpcm9uLmdldCgnQVBNX0NsT1NFX0ZMQUcnLEZhbHNlKSA9PSAnVHJ1ZScpCkVMQVNUSUNfQVBNID0gewogICAgIyBTZXQgdGhlIHJlcXVpcmVkIHNlcnZpY2UgbmFtZS4gQWxsb3dlZCBjaGFyYWN0ZXJzOgogICAgIyBhLXosIEEtWiwgMC05LCAtLCBfLCBhbmQgc3BhY2UKICAgICdTRVJWSUNFX05BTUUnOiAnZGphbmdvLXRlc3QnLAogICAgIyBTZXQgdGhlIGN1c3RvbSBBUE0gU2VydmVyIFVSTCAoZGVmYXVsdDogaHR0cDovL2xvY2FsaG9zdDo4MjAwKQogICAgJ1NFUlZFUl9VUkwnOiBvcy5lbnZpcm9uLmdldCgnQVBNX1NFUlZFUl9VUkwnLCAnJyksCgogICAgIyBTZXQgdGhlIHNlcnZpY2UgZW52aXJvbm1lbnQKCiAgICAjICdERUJVRyc6IG9zLmVudmlyb24uZ2V0KCdBUE1fQ2xPU0VfRkxBRycsRmFsc2UpID09ICdUcnVlJywKICAgICdESVNBQkxFX1NFTkQnOiBvcy5lbnZpcm9uLmdldCgnQVBNX0NsT1NFX0ZMQUcnLFRydWUpID09ICdUcnVlJywKICAgICdDQVBUVVJFX0JPRFknOiAnYWxsJywKICAgICdQUk9DRVNTT1JTJzogWwogICAgICAgICdjb3JlLmFwbV9wcm9jZXNzb3JzLnNhbml0aXplX3Byb2Nlc3NvcicsCiAgICAgICAgJ2VsYXN0aWNhcG0ucHJvY2Vzc29ycy5zYW5pdGl6ZV9zdGFja3RyYWNlX2xvY2FscycsCiAgICAgICAgJ2VsYXN0aWNhcG0ucHJvY2Vzc29ycy5zYW5pdGl6ZV9odHRwX3JlcXVlc3RfY29va2llcycsCiAgICAgICAgJ2VsYXN0aWNhcG0ucHJvY2Vzc29ycy5zYW5pdGl6ZV9odHRwX2hlYWRlcnMnLAogICAgICAgICdlbGFzdGljYXBtLnByb2Nlc3NvcnMuc2FuaXRpemVfaHR0cF93c2dpX2VudicsCiAgICAgICAgJ2VsYXN0aWNhcG0ucHJvY2Vzc29ycy5zYW5pdGl6ZV9odHRwX3JlcXVlc3RfYm9keScsCiAgICBdLAogICAgJ0lOU1RSVU1FTlQnOiBUcnVlCn0KCkNFTEVSWV9CUk9LRVJfVVJMID0gIGYnYW1xcDovL3tjb25maWcuUkFCQklUTVFfVVNFUk5BTUV9Ontjb25maWcuUkFCQklUTVFfUEFTU1dPUkR9QHtjb25maWcuUkFCQklUTVFfSE9TVH06e2NvbmZpZy5SQUJCSVRNUV9QT1JUfS97Y29uZmlnLlJBQkJJVE1RX1ZJUlRVQUxfSE9TVH0nCkNFTEVSWV9SRVNVTFRfQkFDS0VORCA9ICdycGM6Ly8nCgpDRUxFUllfQUNDRVBUX0NPTlRFTlQgPSBbJ2pzb24nXQpDRUxFUllfVEFTS19TRVJJQUxJWkVSID0gJ2pzb24nCkNFTEVSWV9SRVNVTFRfU0VSSUFMSVpFUiA9ICdqc29uJwpDRUxFUllfVElNRVpPTkUgPSAnVVRDJwoKZnJvbSB1cmxsaWIucGFyc2UgaW1wb3J0IHF1b3RlX3BsdXMKClJFRElTX0hPU1QgPSBvcy5nZXRlbnYoIlJFRElTX0hPU1QiLCAiMTI3LjAuMC4xIikKUkVESVNfUE9SVCA9IG9zLmdldGVudigiUkVESVNfUE9SVCIsICI2Mzc5IikKUkVESVNfUEFTU1dPUkRfUkFXID0gb3MuZ2V0ZW52KCJSRURJU19QQVNTV09SRCIsICIiKQpSRURJU19EQiA9IG9zLmdldGVudigiUkVESVNfREIiLCAiMSIpCgpSRURJU19QQVNTV09SRCA9IHF1b3RlX3BsdXMoUkVESVNfUEFTU1dPUkRfUkFXKQoKUkVESVNfVVJMID0gZiJyZWRpczovLzp7UkVESVNfUEFTU1dPUkR9QHtSRURJU19IT1NUfTp7UkVESVNfUE9SVH0ve1JFRElTX0RCfSIKCkNBQ0hFUyA9IHsKICAgICJkZWZhdWx0IjogewogICAgICAgICJCQUNLRU5EIjogImRqYW5nb19yZWRpcy5jYWNoZS5SZWRpc0NhY2hlIiwKICAgICAgICAiTE9DQVRJT04iOiBSRURJU19VUkwsCiAgICAgICAgIk9QVElPTlMiOiB7CiAgICAgICAgICAgICJDTElFTlRfQ0xBU1MiOiAiZGphbmdvX3JlZGlzLmNsaWVudC5EZWZhdWx0Q2xpZW50IiwKICAgICAgICB9LAogICAgfQp9