import logging
import os
import re
import threading
import traceback
from typing import List
import localstack
from localstack import constants
from localstack.services.install import(ARTIFACTS_REPO,INSTALL_DIR_STEPFUNCTIONS,INSTALL_PATH_STEPFUNCTIONS_JAR,JAR_URLS,SFN_PATCH_URL_PREFIX,Installer,InstallerRepository,add_file_to_jar)
from localstack.utils.common import download,file_exists_not_empty,now,run,save_file
from localstack_ext.bootstrap.installer import(mariadb_installer,mosquitto_installer,postgres_installer,redis_installer,timescaledb_installer)
from localstack_ext.bootstrap.licensing import(api_key_configured,is_enterprise,prepare_environment)
LOG=logging.getLogger(__name__)
RULE_ENGINE_INSTALL_URL="https://github.com/whummer/serverless-iot-offline"
H2_DOWNLOAD_URL="http://www.h2database.com/h2-2019-10-14.zip"
SSL_CERT_URL=f"{ARTIFACTS_REPO}/raw/master/local-certs/server.key"
SSL_CERT_URL_FALLBACK="{api_endpoint}/proxy/localstack.cert.key"
INFRA_DIR=os.path.join(os.path.dirname(localstack.__file__),"infra")
LOCALSTACK_DIR=os.path.dirname(localstack.__file__)
POSTGRES_LIB_FOLDER="/usr/lib/postgresql/11/lib"
SFN_PATCH_PRO_CLASS1="cloud/localstack/PersistenceAspect.class"
SFN_PATCH_PRO_CLASS2="cloud/localstack/PersistenceContext.class"
SFN_PATCH_PRO_CLASS3="cloud/localstack/PersistenceState.class"
SFN_PATCH_PRO_CLASS4="cloud/localstack/PersistenceRegionState.class"
SFN_PATCH_PRO_FILE_METAINF="META-INF/aop-pro.xml"
MAVEN_REPO="https://repo1.maven.org/maven2"
URL_KRYO=f"{MAVEN_REPO}/com/esotericsoftware/kryo/5.2.0/kryo-5.2.0.jar"
URL_OBJENESIS=f"{MAVEN_REPO}/org/objenesis/objenesis/3.2/objenesis-3.2.jar"
URL_MINLOG=f"{MAVEN_REPO}/com/esotericsoftware/minlog/1.3.1/minlog-1.3.1.jar"
URL_REFLECTASM=f"{MAVEN_REPO}/com/esotericsoftware/reflectasm/1.11.9/reflectasm-1.11.9.jar"
PRO_JAR_URLS=[URL_KRYO,URL_OBJENESIS,URL_MINLOG,URL_REFLECTASM]
INSTALL_LOCK=threading.RLock()
def install_libs():
 install_iot_rule_engine()
 postgres_installer.install(raise_on_error=False)
 timescaledb_installer.install(raise_on_error=False)
 redis_installer.install(raise_on_error=False)
 mosquitto_installer.install(raise_on_error=False)
 install_stepfunctions()
def install_iot_rule_engine():
 target_dir=LOCALSTACK_DIR
 main_file=os.path.join(target_dir,"node_modules","serverless-iot-offline","query.js")
 if not os.path.exists(main_file):
  LOG.info("Installing IoT rule engine. This may take a while.")
  run("cd %s; npm install %s"%(target_dir,RULE_ENGINE_INSTALL_URL))
 return main_file
def install_stepfunctions():
 if not os.path.exists(INSTALL_PATH_STEPFUNCTIONS_JAR):
  LOG.warning("StepFunctions JAR not available - skipping installation")
  return
 classes=[SFN_PATCH_PRO_CLASS1,SFN_PATCH_PRO_CLASS2,SFN_PATCH_PRO_CLASS3,SFN_PATCH_PRO_CLASS4,SFN_PATCH_PRO_FILE_METAINF]
 for patch_class in classes:
  patch_url=f"{SFN_PATCH_URL_PREFIX}/{patch_class}"
  add_file_to_jar(patch_class,patch_url,target_jar=INSTALL_PATH_STEPFUNCTIONS_JAR)
 manifest_file=os.path.join(INSTALL_DIR_STEPFUNCTIONS,"META-INF","MANIFEST.MF")
 content=run(["unzip","-p",INSTALL_PATH_STEPFUNCTIONS_JAR,"META-INF/MANIFEST.MF"])
 content=re.sub("Main-Class: .+","Main-Class: cloud.localstack.StepFunctionsStarter",content)
 classpath=" ".join([os.path.basename(jar)for jar in[*PRO_JAR_URLS,*JAR_URLS]])
 content=re.sub(r"Class-Path: (.+ )\. ",f"Class-Path: {classpath} . ",content)
 save_file(manifest_file,content)
 run(["zip",INSTALL_PATH_STEPFUNCTIONS_JAR,"META-INF/MANIFEST.MF"],cwd=INSTALL_DIR_STEPFUNCTIONS)
 for jar_url in PRO_JAR_URLS:
  target=os.path.join(INSTALL_DIR_STEPFUNCTIONS,os.path.basename(jar_url))
  if not file_exists_not_empty(target):
   download(jar_url,target)
def setup_ssl_cert():
 from localstack.services import generic_proxy
 target_file=generic_proxy.get_cert_pem_file_path()
 if os.path.exists(target_file):
  if is_enterprise():
   LOG.debug("Avoiding to update SSL certificate.")
   return
  cache_duration_secs=6*60*60
  mod_time=os.path.getmtime(target_file)
  if mod_time>(now()-cache_duration_secs):
   LOG.debug("Using cached SSL certificate (less than 6hrs since last update).")
   return
 LOG.debug("Attempting to download local SSL certificate file")
 timeout_gh=3
 timeout_proxy=5
 try:
  return download_github_artifact(SSL_CERT_URL,target_file,timeout=timeout_gh)
 except Exception:
  url=SSL_CERT_URL_FALLBACK.format(api_endpoint=constants.API_ENDPOINT)
  try:
   return download(url,target_file,timeout=timeout_proxy)
  except Exception as e:
   LOG.info("Unable to download local test SSL certificate from %s to %s (using self-signed cert as fallback): %s",url,target_file,e)
   raise
def download_github_artifact(url:str,target_file:str,timeout:int=None):
 def do_download(url,print_error=False):
  try:
   download(url,target_file,timeout=timeout)
   return True
  except Exception as e:
   if print_error:
    LOG.info("Unable to download Github artifact from from %s to %s: %s %s"%(url,target_file,e,traceback.format_exc()))
 result=do_download(url)
 if not result:
  url=url.replace("https://github.com","https://cdn.jsdelivr.net/gh")
  url=url.replace("/raw/master/","@master/")
  do_download(url,True)
def install_azure():
 from localstack_ext.services.azure import api_specs
 api_specs.download_api_specs()
def install_ecr():
 from localstack_ext.services.ecr import registry
 registry.get_registry_binary()
def install_pysiddhi():
 from localstack_ext.services.kinesisanalytics import query_utils
 query_utils.setup_siddhi()
def install_neptune():
 from localstack_ext.services.neptune import neptune_api
 neptune_api.install_graphdb()
def install_qldb():
 from localstack_ext.services.qldb import partiql
 partiql.install_partiql()
def install_k3d():
 from localstack_ext.services.eks import k8s_utils
 k8s_utils.KubeProviderK3S().initialize()
def install_kafka():
 from localstack_ext.services.kafka.provider import download_kafka
 download_kafka()
class ExtInstallerRepository(InstallerRepository):
 name="ext"
 def should_load(self)->bool:
  return api_key_configured()
 def load(self,*args,**kwargs):
  LOG.debug("Preparing Pro environment for LocalStack Package Manager.")
  with prepare_environment():
   LOG.debug("Pro environment has successfully been prepared.")
 def get_installer(self)->List[Installer]:
  return[("iot-rule-engine",install_iot_rule_engine),("postgres",postgres_installer.install),("timescaledb",timescaledb_installer.install),("redis",redis_installer.install),("mqtt",mosquitto_installer.install),("azure",install_azure),("ecr",install_ecr),("mysql",mariadb_installer.install),("pysiddhi",install_pysiddhi),("neptune",install_neptune),("qldb",install_qldb),("k3d",install_k3d),("kafka",install_kafka),("stepfunctions",install_stepfunctions)]
# Created by pyminifier (https://github.com/liftoff/pyminifier)
