Пример #1
0
def install_kinesis_mock(bin_file_path: str = None):
    response = requests.get(KINESIS_MOCK_RELEASE_URL)
    if not response.ok:
        raise ValueError(
            f"Could not get list of releases from {KINESIS_MOCK_RELEASE_URL}: {response.text}"
        )

    bin_file_path = bin_file_path or kinesis_mock_install_path()
    github_release = response.json()
    download_url = None
    bin_file_name = os.path.basename(bin_file_path)
    for asset in github_release.get("assets", []):
        # find the correct binary in the release
        if asset["name"] == bin_file_name:
            download_url = asset["browser_download_url"]
            break
    if download_url is None:
        raise ValueError(
            f"Could not find required binary {bin_file_name} in release {KINESIS_MOCK_RELEASE_URL}"
        )

    mkdir(INSTALL_DIR_KINESIS_MOCK)
    LOG.info("downloading kinesis-mock binary from %s", download_url)
    download(download_url, bin_file_path)
    chmod_r(bin_file_path, 0o777)
Пример #2
0
def download_and_extract(archive_url,
                         target_dir,
                         retries=0,
                         sleep=3,
                         tmp_archive=None):
    mkdir(target_dir)

    _, ext = os.path.splitext(tmp_archive or archive_url)

    tmp_archive = tmp_archive or new_tmp_file()
    if not os.path.exists(tmp_archive) or os.path.getsize(tmp_archive) <= 0:
        # create temporary placeholder file, to avoid duplicate parallel downloads
        save_file(tmp_archive, "")
        for i in range(retries + 1):
            try:
                download(archive_url, tmp_archive)
                break
            except Exception:
                time.sleep(sleep)

    if ext == ".zip":
        unzip(tmp_archive, target_dir)
    elif ext in [".bz2", ".gz", ".tgz"]:
        untar(tmp_archive, target_dir)
    else:
        raise Exception(f"Unsupported archive format: {ext}")
Пример #3
0
def save_for_retrospection(id: str, region: str, **kwargs: Dict[str, Any]):
    """Save a message for retrospection.

    The email is saved to filesystem and is also made accessible via a service endpoint.

    kwargs should consist of following keys related to the email:
    - Body
    - Destinations
    - RawData
    - Source
    - Subject
    - Template
    - TemplateData
    """
    ses_dir = os.path.join(config.dirs.data or config.dirs.tmp, "ses")

    mkdir(ses_dir)
    path = os.path.join(ses_dir, id + ".json")

    email = {"Id": id, "Region": region, **kwargs}

    EMAILS[id] = email

    def _serialize(obj):
        """JSON serializer for timestamps."""
        if isinstance(obj, (datetime, date, time)):
            return obj.isoformat()
        return obj.__dict__

    with open(path, "w") as f:
        f.write(json.dumps(email, default=_serialize))

    LOGGER.debug("Email saved at: %s", path)
Пример #4
0
def install_local_kms():
    local_arch = f"{platform.system().lower()}-{get_arch()}"
    binary_path = INSTALL_PATH_KMS_BINARY_PATTERN.replace("<arch>", local_arch)
    if not os.path.exists(binary_path):
        log_install_msg("KMS")
        mkdir(INSTALL_DIR_KMS)
        kms_url = KMS_URL_PATTERN.replace("<arch>", local_arch)
        download(kms_url, binary_path)
        chmod_r(binary_path, 0o777)
Пример #5
0
def install_elasticmq():
    # TODO remove this function if we stop using ElasticMQ entirely
    if not os.path.exists(INSTALL_PATH_ELASTICMQ_JAR):
        log_install_msg("ElasticMQ")
        mkdir(INSTALL_DIR_ELASTICMQ)
        # download archive
        tmp_archive = os.path.join(config.dirs.tmp, "elasticmq-server.jar")
        if not os.path.exists(tmp_archive):
            download(ELASTICMQ_JAR_URL, tmp_archive)
        shutil.copy(tmp_archive, INSTALL_DIR_ELASTICMQ)
Пример #6
0
def install_opensearch(version=None):
    # locally import to avoid having a dependency on ASF when starting the CLI
    from localstack.aws.api.opensearch import EngineType
    from localstack.services.opensearch import versions

    if not version:
        version = OPENSEARCH_DEFAULT_VERSION

    version = get_opensearch_install_version(version)
    install_dir = get_opensearch_install_dir(version)
    installed_executable = os.path.join(install_dir, "bin", "opensearch")
    if not os.path.exists(installed_executable):
        with OS_INSTALL_LOCKS.setdefault(version, threading.Lock()):
            if not os.path.exists(installed_executable):
                log_install_msg(f"OpenSearch ({version})")
                opensearch_url = versions.get_download_url(
                    version, EngineType.OpenSearch)
                install_dir_parent = os.path.dirname(install_dir)
                mkdir(install_dir_parent)
                # download and extract archive
                tmp_archive = os.path.join(
                    config.dirs.tmp,
                    f"localstack.{os.path.basename(opensearch_url)}")
                download_and_extract_with_retry(opensearch_url, tmp_archive,
                                                install_dir_parent)
                opensearch_dir = glob.glob(
                    os.path.join(install_dir_parent, "opensearch*"))
                if not opensearch_dir:
                    raise Exception(
                        f"Unable to find OpenSearch folder in {install_dir_parent}"
                    )
                shutil.move(opensearch_dir[0], install_dir)

                for dir_name in ("data", "logs", "modules", "plugins",
                                 "config/scripts"):
                    dir_path = os.path.join(install_dir, dir_name)
                    mkdir(dir_path)
                    chmod_r(dir_path, 0o777)

    # patch JVM options file - replace hardcoded heap size settings
    jvm_options_file = os.path.join(install_dir, "config", "jvm.options")
    if os.path.exists(jvm_options_file):
        jvm_options = load_file(jvm_options_file)
        jvm_options_replaced = re.sub(r"(^-Xm[sx][a-zA-Z0-9.]+$)",
                                      r"# \1",
                                      jvm_options,
                                      flags=re.MULTILINE)
        if jvm_options != jvm_options_replaced:
            save_file(jvm_options_file, jvm_options_replaced)
Пример #7
0
def install_amazon_kinesis_client_libs():
    # install KCL/STS JAR files
    if not os.path.exists(INSTALL_PATH_KCL_JAR):
        mkdir(INSTALL_DIR_KCL)
        tmp_archive = os.path.join(tempfile.gettempdir(),
                                   "aws-java-sdk-sts.jar")
        if not os.path.exists(tmp_archive):
            download(STS_JAR_URL, tmp_archive)
        shutil.copy(tmp_archive, INSTALL_DIR_KCL)
    # Compile Java files
    from localstack.utils.kinesis import kclipy_helper

    classpath = kclipy_helper.get_kcl_classpath()

    if is_windows():
        classpath = re.sub(r":([^\\])", r";\1", classpath)
    java_files = f"{MODULE_MAIN_PATH}/utils/kinesis/java/cloud/localstack/*.java"
    class_files = f"{MODULE_MAIN_PATH}/utils/kinesis/java/cloud/localstack/*.class"
    if not glob.glob(class_files):
        run(f'javac -source {JAVAC_TARGET_VERSION} -target {JAVAC_TARGET_VERSION} -cp "{classpath}" {java_files}'
            )
Пример #8
0
def prepare_docker_start():
    # prepare environment for docker start
    container_name = config.MAIN_CONTAINER_NAME

    if DOCKER_CLIENT.is_container_running(container_name):
        raise ContainerExists(
            'LocalStack container named "%s" is already running' %
            container_name)
    if config.dirs.tmp != config.dirs.functions and not config.LAMBDA_REMOTE_DOCKER:
        # Logger is not initialized at this point, so the warning is displayed via print
        print(
            f"WARNING: The detected temp folder for localstack ({config.dirs.tmp}) is not equal to the "
            f"HOST_TMP_FOLDER environment variable set ({config.dirs.functions})."
        )

    os.environ[ENV_SCRIPT_STARTING_DOCKER] = "1"

    # make sure temp folder exists
    mkdir(config.dirs.tmp)
    try:
        chmod_r(config.dirs.tmp, 0o777)
    except Exception:
        pass
Пример #9
0
def create_lambda_archive(
    script: str,
    get_content: bool = False,
    libs: List[str] = None,
    runtime: str = None,
    file_name: str = None,
    exclude_func: Callable[[str], bool] = None,
):
    """Utility method to create a Lambda function archive"""
    if libs is None:
        libs = []
    runtime = runtime or LAMBDA_DEFAULT_RUNTIME

    with tempfile.TemporaryDirectory(prefix=ARCHIVE_DIR_PREFIX) as tmp_dir:
        file_name = file_name or get_handler_file_from_name(
            LAMBDA_DEFAULT_HANDLER, runtime=runtime)
        script_file = os.path.join(tmp_dir, file_name)
        if os.path.sep in script_file:
            mkdir(os.path.dirname(script_file))
            # create __init__.py files along the path to allow Python imports
            path = file_name.split(os.path.sep)
            for i in range(1, len(path)):
                save_file(os.path.join(tmp_dir, *(path[:i] + ["__init__.py"])),
                          "")
        save_file(script_file, script)
        chmod_r(script_file, 0o777)
        # copy libs
        for lib in libs:
            paths = [lib, "%s.py" % lib]
            try:
                module = importlib.import_module(lib)
                paths.append(module.__file__)
            except Exception:
                pass
            target_dir = tmp_dir
            root_folder = os.path.join(LOCALSTACK_VENV_FOLDER,
                                       "lib/python*/site-packages")
            if lib == "localstack":
                paths = ["localstack/*.py", "localstack/utils"]
                root_folder = LOCALSTACK_ROOT_FOLDER
                target_dir = os.path.join(tmp_dir, lib)
                mkdir(target_dir)
            for path in paths:
                file_to_copy = path if path.startswith("/") else os.path.join(
                    root_folder, path)
                for file_path in glob.glob(file_to_copy):
                    name = os.path.join(target_dir,
                                        file_path.split(os.path.sep)[-1])
                    if os.path.isdir(file_path):
                        copy_dir(file_path, name)
                    else:
                        shutil.copyfile(file_path, name)

        if exclude_func:
            for dirpath, folders, files in os.walk(tmp_dir):
                for name in list(folders) + list(files):
                    full_name = os.path.join(dirpath, name)
                    relative = os.path.relpath(full_name, start=tmp_dir)
                    if exclude_func(relative):
                        rm_rf(full_name)

        # create zip file
        result = create_zip_file(tmp_dir, get_content=get_content)
        return result
Пример #10
0
def install_lambda_java_testlibs():
    # Download the LocalStack Utils Test jar file from the maven repo
    if not os.path.exists(TEST_LAMBDA_JAVA):
        mkdir(os.path.dirname(TEST_LAMBDA_JAVA))
        download(TEST_LAMBDA_JAR_URL, TEST_LAMBDA_JAVA)
Пример #11
0
def install_stepfunctions_local():
    if not os.path.exists(INSTALL_PATH_STEPFUNCTIONS_JAR):
        # pull the JAR file from the Docker image, which is more up-to-date than the downloadable JAR file
        if not DOCKER_CLIENT.has_docker():
            # TODO: works only when a docker socket is available -> add a fallback if running without Docker?
            LOG.warning(
                "Docker not available - skipping installation of StepFunctions dependency"
            )
            return
        log_install_msg("Step Functions")
        mkdir(INSTALL_DIR_STEPFUNCTIONS)
        DOCKER_CLIENT.pull_image(IMAGE_NAME_SFN_LOCAL)
        docker_name = "tmp-ls-sfn"
        DOCKER_CLIENT.run_container(
            IMAGE_NAME_SFN_LOCAL,
            remove=True,
            entrypoint="",
            name=docker_name,
            detach=True,
            command=["sleep", "15"],
        )
        time.sleep(5)
        DOCKER_CLIENT.copy_from_container(
            docker_name,
            local_path=dirs.static_libs,
            container_path="/home/stepfunctionslocal/")

        path = Path(f"{dirs.static_libs}/stepfunctionslocal/")
        for file in path.glob("*.jar"):
            file.rename(Path(INSTALL_DIR_STEPFUNCTIONS) / file.name)
        rm_rf(str(path))

    classes = [
        SFN_PATCH_CLASS1,
        SFN_PATCH_CLASS2,
        SFN_PATCH_CLASS_REGION,
        SFN_PATCH_CLASS_STARTER,
        SFN_PATCH_CLASS_ASYNC2SERVICEAPI,
        SFN_PATCH_CLASS_DESCRIBEEXECUTIONPARSED,
        SFN_PATCH_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)

    # special case for Manifest file - extract first, replace content, then update in JAR file
    manifest_file = os.path.join(INSTALL_DIR_STEPFUNCTIONS, "META-INF",
                                 "MANIFEST.MF")
    if not os.path.exists(manifest_file):
        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 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,
        )

    # download additional jar libs
    for jar_url in 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)

    # download aws-sdk lambda handler
    target = os.path.join(INSTALL_DIR_STEPFUNCTIONS,
                          "localstack-internal-awssdk", "awssdk.zip")
    if not file_exists_not_empty(target):
        download(SFN_AWS_SDK_LAMBDA_ZIP_FILE, target)
Пример #12
0
def install_elasticsearch(version=None):
    # locally import to avoid having a dependency on ASF when starting the CLI
    from localstack.aws.api.opensearch import EngineType
    from localstack.services.opensearch import versions

    if not version:
        version = ELASTICSEARCH_DEFAULT_VERSION

    version = get_elasticsearch_install_version(version)
    install_dir = get_elasticsearch_install_dir(version)
    installed_executable = os.path.join(install_dir, "bin", "elasticsearch")
    if not os.path.exists(installed_executable):
        log_install_msg(f"Elasticsearch ({version})")
        es_url = versions.get_download_url(version, EngineType.Elasticsearch)
        install_dir_parent = os.path.dirname(install_dir)
        mkdir(install_dir_parent)
        # download and extract archive
        tmp_archive = os.path.join(config.dirs.tmp,
                                   f"localstack.{os.path.basename(es_url)}")
        download_and_extract_with_retry(es_url, tmp_archive,
                                        install_dir_parent)
        elasticsearch_dir = glob.glob(
            os.path.join(install_dir_parent, "elasticsearch*"))
        if not elasticsearch_dir:
            raise Exception(
                f"Unable to find Elasticsearch folder in {install_dir_parent}")
        shutil.move(elasticsearch_dir[0], install_dir)

        for dir_name in ("data", "logs", "modules", "plugins",
                         "config/scripts"):
            dir_path = os.path.join(install_dir, dir_name)
            mkdir(dir_path)
            chmod_r(dir_path, 0o777)

        # install default plugins
        for plugin in ELASTICSEARCH_PLUGIN_LIST:
            plugin_binary = os.path.join(install_dir, "bin",
                                         "elasticsearch-plugin")
            plugin_dir = os.path.join(install_dir, "plugins", plugin)
            if not os.path.exists(plugin_dir):
                LOG.info("Installing Elasticsearch plugin %s", plugin)

                def try_install():
                    output = run([plugin_binary, "install", "-b", plugin])
                    LOG.debug("Plugin installation output: %s", output)

                # We're occasionally seeing javax.net.ssl.SSLHandshakeException -> add download retries
                download_attempts = 3
                try:
                    retry(try_install, retries=download_attempts - 1, sleep=2)
                except Exception:
                    LOG.warning(
                        "Unable to download Elasticsearch plugin '%s' after %s attempts",
                        plugin,
                        download_attempts,
                    )
                    if not os.environ.get("IGNORE_ES_DOWNLOAD_ERRORS"):
                        raise

    # delete some plugins to free up space
    for plugin in ELASTICSEARCH_DELETE_MODULES:
        module_dir = os.path.join(install_dir, "modules", plugin)
        rm_rf(module_dir)

    # disable x-pack-ml plugin (not working on Alpine)
    xpack_dir = os.path.join(install_dir, "modules", "x-pack-ml", "platform")
    rm_rf(xpack_dir)

    # patch JVM options file - replace hardcoded heap size settings
    jvm_options_file = os.path.join(install_dir, "config", "jvm.options")
    if os.path.exists(jvm_options_file):
        jvm_options = load_file(jvm_options_file)
        jvm_options_replaced = re.sub(r"(^-Xm[sx][a-zA-Z0-9.]+$)",
                                      r"# \1",
                                      jvm_options,
                                      flags=re.MULTILINE)
        if jvm_options != jvm_options_replaced:
            save_file(jvm_options_file, jvm_options_replaced)