예제 #1
0
    def before_request(self, name, *args, **kwargs):
        ctx = app.app_context()
        ctx.g.xid = token_hex(8)
        http = HttpResponse()
        request_uri = request.full_path
        # add here your custom header to be logged with fluentd
        self.message_dumper.set_header(
            HeaderConstants.X_REQUEST_ID,
            request.headers.get(HeaderConstants.X_REQUEST_ID) if
            request.headers.get(HeaderConstants.X_REQUEST_ID) else ctx.g.xid)
        self.message_dumper.set_header(HeaderConstants.REQUEST_URI,
                                       request_uri)

        response = self.fluentd.emit(
            tag="api", msg=self.message_dumper.dump(request=request))
        app.logger.debug(response)
        if not str(request.headers.get(HeaderConstants.TOKEN)) == str(
                EnvStartupSingleton.get_instance().get_config_env_vars().get(
                    EnvConstants.HTTP_AUTH_TOKEN)):
            if not ("/apidocs" in request_uri or "/swagger/swagger.json"
                    in request_uri):  # exclude swagger
                headers = {
                    HeaderConstants.X_REQUEST_ID:
                    self.message_dumper.get_header(
                        HeaderConstants.X_REQUEST_ID)
                }
                return Response(json.dumps(
                    http.response(
                        ApiCode.UNAUTHORIZED.value,
                        ErrorMessage.HTTP_CODE.get(ApiCode.UNAUTHORIZED.value),
                        "Invalid Token")),
                                401,
                                mimetype="application/json",
                                headers=headers)
예제 #2
0
 def __enrichlog(level_code, msg):
     return {
         "name": properties.get('name'),
         "port": EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.PORT),
         "version": properties.get('version'),
         "uname": list(platform.uname()),
         "python": platform.python_version(),
         "pid": os.getpid(),
         "level_code": level_code,
         "msg": msg,
         "timestamp": str(datetime.datetime.now()),
     }
예제 #3
0
    def register_app(self, app_ip_port, app_append_label):
        app_ip = app_ip_port.split(":")[0]
        app_port = int(app_ip_port.split(":")[1])
        print("Starting eureka register on eureka server " + self.host + ".\n")
        print(properties['name'] + " registering with: ip=" + app_ip + ",  port=" + str(app_port) + "... \n")

        protocol = "https" if EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.HTTPS_ENABLE) \
            else "http"

        eureka_client.init(eureka_server=f"{self.host}",
                           app_name=f"{properties['name']}{app_append_label}",
                           instance_port=app_port,
                           instance_secure_port=app_port,
                           instance_ip=app_ip,
                           home_page_url=f"{protocol}://{app_ip}:{app_port}/{EnvInit.init.get(EnvConstants.DEPLOY_WITH)}/",
                           health_check_url=f"{protocol}://{app_ip}:{app_port}/{EnvInit.init.get(EnvConstants.DEPLOY_WITH)}/ping",
                           status_page_url=f"{protocol}://{app_ip}:{app_port}/{EnvInit.init.get(EnvConstants.DEPLOY_WITH)}/about"
                           )
    def register_app(self, app_ip, app_port):
        print("Starting eureka register on eureka server {}. \n".format(
            self.host))
        print("{} registering with: ip={}, port={} \n".format(
            properties['name'], app_ip, app_port))

        protocol = "https" if EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.HTTPS_ENABLE) \
            else "http"

        eureka_client.init(
            eureka_server=f"{self.host}",
            app_name=f"{properties['name']}",
            instance_port=app_port,
            instance_secure_port=app_port,
            instance_ip=app_ip,
            home_page_url=f"{protocol}://{app_ip}:{app_port}/",
            health_check_url=f"{protocol}://{app_ip}:{app_port}/ping",
            status_page_url=f"{protocol}://{app_ip}:{app_port}/ping")
예제 #5
0
def get_eureka_apps_name(eureka_app_name):
    eureka_app_name = eureka_app_name.strip()
    eureka = Eureka(
        EnvStartupSingleton.get_instance().get_config_env_vars().get(
            EnvConstants.EUREKA_SERVER))

    try:
        apps_list = eureka.get_type_eureka_apps(eureka_app_name)
    except Exception as e:
        raise ApiException(
            ApiCode.GET_EUREKA_APPS_FAILED.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.GET_EUREKA_APPS_FAILED.value) %
            eureka.get_eureka_host(), e)
    return Response(json.dumps(HttpResponse().response(
        ApiCode.SUCCESS.value,
        ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), apps_list)),
                    200,
                    mimetype="application/json")
예제 #6
0
class DockerView(FlaskView):
    logger = \
        sender.FluentSender(tag=properties.get('name'),
                            host=EnvStartupSingleton.get_instance().get_config_env_vars().get(
                                EnvConstants.FLUENTD_IP_PORT).split(":")[0],
                            port=int(EnvStartupSingleton.get_instance().get_config_env_vars().get(
                                EnvConstants.FLUENTD_IP_PORT).split(":")[1])) \
            if EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.FLUENTD_IP_PORT) else None
    fluentd = Fluentd(logger)
    message_dumper = MessageDumper()

    def before_request(self, name, *args, **kwargs):
        ctx = app.app_context()
        ctx.g.xid = token_hex(8)
        http = HttpResponse()
        request_uri = request.full_path
        # add here your custom header to be logged with fluentd
        self.message_dumper.set_header(
            HeaderConstants.X_REQUEST_ID,
            request.headers.get(HeaderConstants.X_REQUEST_ID) if
            request.headers.get(HeaderConstants.X_REQUEST_ID) else ctx.g.xid)
        self.message_dumper.set_header(HeaderConstants.REQUEST_URI,
                                       request_uri)

        response = self.fluentd.emit(
            tag="api", msg=self.message_dumper.dump(request=request))
        app.logger.debug(response)
        if not str(request.headers.get(HeaderConstants.TOKEN)) == str(
                EnvStartupSingleton.get_instance().get_config_env_vars().get(
                    EnvConstants.HTTP_AUTH_TOKEN)):
            if not ("/apidocs" in request_uri or "/swagger/swagger.json"
                    in request_uri):  # exclude swagger
                headers = {
                    HeaderConstants.X_REQUEST_ID:
                    self.message_dumper.get_header(
                        HeaderConstants.X_REQUEST_ID)
                }
                return Response(json.dumps(
                    http.response(
                        ApiCode.UNAUTHORIZED.value,
                        ErrorMessage.HTTP_CODE.get(ApiCode.UNAUTHORIZED.value),
                        "Invalid Token")),
                                401,
                                mimetype="application/json",
                                headers=headers)

    def after_request(self, name, http_response):
        http_response.headers[
            HeaderConstants.X_REQUEST_ID] = self.message_dumper.get_header(
                HeaderConstants.X_REQUEST_ID)

        http_response.direct_passthrough = False
        response = self.fluentd.emit(
            tag="api", msg=self.message_dumper.dump(http_response))
        app.logger.debug(f"{response}")

        return http_response

    @classmethod
    def handle_api_error(cls, e):
        http_response = Response(json.dumps(HttpResponse().response(
            code=e.code,
            message=e.message,
            description="Exception({})".format(e.exception.__str__()))),
                                 500,
                                 mimetype="application/json")
        http_response.headers[
            HeaderConstants.
            X_REQUEST_ID] = DockerView.message_dumper.get_header(
                HeaderConstants.X_REQUEST_ID)
        response = DockerView.fluentd.emit(
            tag="api", msg=DockerView.message_dumper.dump(http_response))
        app.logger.debug(f"{response}")
        return http_response

    def index(self):
        return render_template('index.html')

    @route('/apidocs')
    def send_swagger(self):
        return render_template('index.html')

    @route('/resources/<path:path>')
    def send_resources(self, path):
        return send_from_directory('swaggerui/resources', path)

    @route('/swagger/swagger.json')
    def swagger(self):
        return render_template('swagger.json')

    @route('/ping')
    def ping(self):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), "pong")),
                        200,
                        mimetype="application/json")

    @route('/about')
    def about(self):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), about_system)),
                        200,
                        mimetype="application/json")

    @route('/env', methods=['GET'])
    def get_env_vars(self):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            EnvironmentSingleton.get_instance().get_env_and_virtual_env())),
                        200,
                        mimetype="application/json")

    @route('/envinit', methods=['GET'])
    def get_init_env(self):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), EnvInit.init)),
                        200,
                        mimetype="application/json")

    @route('/env/<env_var>', methods=['GET'])
    def get_env_var(self, env_var):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            EnvironmentSingleton.get_instance().get_env_and_virtual_env().get(
                env_var))),
                        200,
                        mimetype="application/json")

    @route('/env', methods=['POST'])
    def set_env(self):
        input_data = request.data.decode("UTF-8", "replace").strip()

        try:
            env_vars_attempted = json.loads(input_data)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.INVALID_JSON_PAYLOAD.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.INVALID_JSON_PAYLOAD.value)
                % str(input_data), e)

        try:
            env_vars_added = EnvironmentSingleton.get_instance().set_env_vars(
                env_vars_attempted)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.SET_ENV_VAR_FAILURE,
                ErrorMessage.HTTP_CODE.get(ApiCode.SET_ENV_VAR_FAILURE) %
                str(input_data), e)
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            env_vars_added)),
                        200,
                        mimetype="application/json")

    @route('/render/<template>/<variables>', methods=['GET', 'POST'])
    def get_rendered_content_with_env(self, template, variables):
        try:
            input_json = request.get_json(force=True)
            for key, value in input_json.items():
                if key not in EnvironmentSingleton.get_instance().get_env():
                    EnvironmentSingleton.get_instance().set_env_var(
                        str(key), str(value))
        except Exception as e:
            app.logger.debug({
                "msg":
                f"Could not load the body from the request as JSON: {e.__str__()}"
            })

        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.TEMPLATE, template.strip())
        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.VARIABLES, variables.strip())

        try:
            env_vars = EnvironmentSingleton.get_instance(
            ).get_env_and_virtual_env()
            rendered_content = Render(
                env_vars.get(EnvConstants.TEMPLATE),
                env_vars.get(EnvConstants.VARIABLES)).rend_template()
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.JINJA2_RENDER_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.JINJA2_RENDER_FAILURE.value), e)

        return Response(rendered_content, 200, mimetype="text/plain")

    @route('/deployments', methods=['GET'])
    def get_active_deployments(self):
        docker_utils = DockerUtils()
        active_deployments = docker_utils.get_active_deployments()
        app.logger.debug(
            {"msg": {
                "active_deployments": f"{len(active_deployments)}"
            }})

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            active_deployments)),
                        200,
                        mimetype="application/json")

    @route('/deployments/prepare', methods=['PUT'])
    def receive_prepared_deployment_and_unpack(self):
        token = token_hex(8)
        io_utils = IOUtils()
        deployment_id = request.headers.get("Deployment-Id").lower(
        ) if request.headers.get("Deployment-Id") else token
        deploy_dir = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{deployment_id}"
        file_path = f"{deploy_dir}/archive.zip"
        file_content = request.get_data()
        # send here the complete env. The deployment template can be overridden at deploy start
        if not file_content:
            raise ApiExceptionDocker(
                ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value),
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value))

        try:
            io_utils.create_dir(deploy_dir)
            io_utils.write_to_file_binary(file_path, file_content)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.UPLOAD_FILE_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.UPLOAD_FILE_FAILURE.value),
                e)
        try:
            shutil.unpack_archive(file_path, deploy_dir)
            io_utils.remove_file(file_path)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.FOLDER_UNZIP_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.FOLDER_UNZIP_FAILURE.value),
                e)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), deployment_id)),
                        200,
                        mimetype="application/json")

    @route('/deployments/cleanup', methods=['DELETE'])
    def cleanup_inactive_deployments(self):
        """ ! can be executed only after /deployments DELETE ! """
        try:
            deleted_folders = DockerUtils.folder_clean_up()
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.DEPLOYMENTS_FOLDER_CLEANUP_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.DEPLOYMENTS_FOLDER_CLEANUP_FAILURE.value), e)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            deleted_folders)),
                        200,
                        mimetype="application/json")

    @route('/deployments', methods=['POST'])
    def start_deployment(self):
        docker_utils = DockerUtils()
        token = token_hex(8)
        deployment_id = request.headers.get("Deployment-Id").lower(
        ) if request.headers.get("Deployment-Id") else token
        deploy_dir = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{deployment_id}"
        file = f"{deploy_dir}/docker-compose.yml"
        header_key = 'Eureka-Server'
        eureka_server_header = request.headers.get(f"{header_key}")
        config_env_vars = EnvStartupSingleton.get_instance(
        ).get_config_env_vars()
        input_data = request.data.decode('UTF-8').strip()

        status = CmdUtils.run_cmd_shell_false(["docker", "ps"])
        if "Cannot connect to the Docker daemon".lower() in status.get(
                'err').lower():
            raise ApiExceptionDocker(
                ApiCode.DOCKER_DAEMON_NOT_RUNNING.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.DOCKER_DAEMON_NOT_RUNNING.value),
                status.get('err'))

        active_deployments = docker_utils.get_active_deployments()
        if len(active_deployments) >= EnvInit.init.get(
                EnvConstants.MAX_DEPLOYMENTS):
            raise ApiExceptionDocker(
                ApiCode.MAX_DEPLOYMENTS_REACHED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.MAX_DEPLOYMENTS_REACHED.value) %
                str(EnvInit.init.get(EnvConstants.MAX_DEPLOYMENTS)),
                active_deployments)
        try:
            template_file_name = f"deployment_{deployment_id}.yml"
            template_file_path = f"{EnvInit.init.get(EnvConstants.TEMPLATES_DIR)}/{template_file_name}"
            app.logger.debug({
                "msg": {
                    "file": template_file_path,
                    "file_content": f"{input_data}"
                }
            })
            IOUtils.write_to_file(template_file_path, input_data)

            IOUtils.create_dir(deploy_dir)
            EnvironmentSingleton.get_instance().set_env_var(
                EnvConstants.TEMPLATE, template_file_name)
            env_vars = EnvironmentSingleton.get_instance(
            ).get_env_and_virtual_env()
            render = Render(env_vars.get(EnvConstants.TEMPLATE),
                            env_vars.get(EnvConstants.VARIABLES))
            if config_env_vars.get(
                    EnvConstants.EUREKA_SERVER) and config_env_vars.get(
                        EnvConstants.APP_IP_PORT):
                # if {{app_ip_port}} and {{eureka_server}} then register that instance too
                if '{{app_ip_port}}' in input_data and '{{eureka_server}}' in input_data:
                    eureka_server = config_env_vars.get(
                        EnvConstants.EUREKA_SERVER)
                    # header value overwrite the eureka server
                    if eureka_server_header:
                        eureka_server = eureka_server_header
                    input_data = render.get_jinja2env().get_template(
                        env_vars.get(EnvConstants.TEMPLATE)).render({
                            "deployment_id":
                            f"{deployment_id}",
                            "eureka_server":
                            eureka_server,
                            "app_ip_port":
                            config_env_vars.get(
                                EnvConstants.APP_IP_PORT).split("/")[0]
                        })
            os.remove(template_file_path) if os.path.exists(
                template_file_path) else None
            app.logger.debug(
                {"msg": {
                    "file": file,
                    "file_content": f"{input_data}"
                }})
            IOUtils.write_to_file(file, input_data) if input_data else None
            CmdUtils.run_cmd_detached(
                rf'''docker-compose -f {file} pull && docker-compose -f {file} up -d'''
            )
        except Exception as e:
            app.logger.debug({"msg": docker_utils.down(file)})
            raise ApiExceptionDocker(
                ApiCode.DEPLOY_START_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_START_FAILURE.value),
                e)

        DeploymentMetadataSingleton.get_instance() \
            .delete_metadata_for_inactive_deployments(DockerUtils.get_active_deployments())
        metadata = DeploymentReader.get_metadata_for_deployment(
            IOUtils.read_file(file=file))
        IOUtils.write_to_file_dict(f"{deploy_dir}/metadata.json", metadata)
        DeploymentMetadataSingleton.get_instance().set_metadata_for_deployment(
            deployment_id, metadata)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), deployment_id)),
                        200,
                        mimetype="application/json")

    @route('/deployments/<template>/<variables>', methods=['POST'])
    def start_deployment_with_templates(self, template, variables):
        http = HttpResponse()
        docker_utils = DockerUtils()
        token = token_hex(8)
        deployment_id = request.headers.get("Deployment-Id").lower(
        ) if request.headers.get("Deployment-Id") else token
        deploy_dir = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{deployment_id}"
        file = f"{deploy_dir}/docker-compose.yml"

        try:
            input_json = request.get_json(force=True)
            for key, value in input_json.items():
                if key not in EnvironmentSingleton.get_instance().get_env():
                    EnvironmentSingleton.get_instance().set_env_var(
                        str(key), str(value))
        except Exception as e:
            app.logger.debug(
                f"Could not parse the input from the request as JSON: {e.__str__()}"
            )

        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.TEMPLATE, template.strip())
        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.VARIABLES, variables.strip())
        env_vars = EnvironmentSingleton.get_instance().get_env_and_virtual_env(
        )
        app.logger.debug(
            {"msg": {
                "template_file": env_vars.get(EnvConstants.TEMPLATE)
            }})
        app.logger.debug(
            {"msg": {
                "variables_file": env_vars.get(EnvConstants.VARIABLES)
            }})

        status = CmdUtils.run_cmd_shell_false(["docker", "ps"])
        if "Cannot connect to the Docker daemon".lower() in status.get(
                'err').lower():
            raise ApiExceptionDocker(
                ApiCode.DOCKER_DAEMON_NOT_RUNNING.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.DOCKER_DAEMON_NOT_RUNNING.value),
                status.get('err'))

        active_deployments = docker_utils.get_active_deployments()
        if len(active_deployments) >= EnvInit.init.get(
                EnvConstants.MAX_DEPLOYMENTS):
            raise ApiExceptionDocker(
                ApiCode.MAX_DEPLOYMENTS_REACHED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.MAX_DEPLOYMENTS_REACHED.value) %
                str(EnvInit.init.get(EnvConstants.MAX_DEPLOYMENTS)),
                active_deployments)
        try:
            r = Render(env_vars.get(EnvConstants.TEMPLATE),
                       env_vars.get(EnvConstants.VARIABLES))
            IOUtils.create_dir(deploy_dir)
            IOUtils.write_to_file(file, r.rend_template())
            CmdUtils.run_cmd_detached(
                rf'''docker-compose -f {file} pull && docker-compose -f {file} up -d'''
            )
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.DEPLOY_START_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_START_FAILURE.value),
                e)

        DeploymentMetadataSingleton.get_instance() \
            .delete_metadata_for_inactive_deployments(DockerUtils.get_active_deployments())
        metadata = DeploymentReader.get_metadata_for_deployment(
            IOUtils.read_file(file=file))
        IOUtils.write_to_file_dict(f"{deploy_dir}/metadata.json", metadata)
        DeploymentMetadataSingleton.get_instance().set_metadata_for_deployment(
            deployment_id, metadata)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          deployment_id)),
                        200,
                        mimetype="application/json")

    @route('/deployments/<env_id>', methods=['GET'])
    def get_deployment_status(self, env_id):
        env_id = env_id.lower()
        docker_utils = DockerUtils()
        env_id = env_id.strip()

        try:
            status = docker_utils.ps(env_id)
            if "Cannot connect to the Docker daemon".lower() in status.get(
                    'err').lower():
                raise Exception(status.get('err'))
            result = status.get('out').split("\n")[1:]
            app.logger.debug({"msg": status})
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.DEPLOY_STATUS_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.DEPLOY_STATUS_FAILURE.value), e)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            ActiveDeployment.docker_deployment(env_id, result))),
                        200,
                        mimetype="application/json")

    @route('/deployments/<depl_id>', methods=['DELETE'])
    def delete_deployment_id(self, depl_id):
        depl_id = depl_id.strip()
        docker_utils = DockerUtils()
        depl_folder = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{depl_id}"
        file = f"{depl_folder}/docker-compose.yml"
        try:
            status = docker_utils.down(file)
            if "Cannot connect to the Docker daemon".lower() in status.get(
                    'err').lower():
                raise Exception(status.get('err'))
            app.logger.debug({"msg": status})
            status = docker_utils.ps(depl_id)
            result = status.get('out').split("\n")[1:]
            DeploymentMetadataSingleton.get_instance(
            ).delete_metadata_for_deployment(depl_id)
            IOUtils.remove_directory(f"{depl_folder}")
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.DEPLOY_STOP_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_STOP_FAILURE.value),
                e)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), result)),
                        200,
                        mimetype="application/json")

    @route('/deployments', methods=['DELETE'])
    def delete_all_deployments(self):
        docker_utils = DockerUtils()
        try:
            active_deployments = docker_utils.get_active_deployments()
            for deployment in active_deployments:
                depl_id = deployment.get('id')
                depl_folder = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{depl_id}"
                status = docker_utils.down(f"{depl_folder}/docker-compose.yml")
                if "Cannot connect to the Docker daemon".lower() in status.get(
                        'err').lower():
                    raise Exception(status.get('err'))
                app.logger.debug({"msg": status})
                DeploymentMetadataSingleton.get_instance(
                ).delete_metadata_for_deployment(depl_id)

            DockerUtils.folder_clean_up()
            result = docker_utils.get_active_deployments()
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.DEPLOY_STOP_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_STOP_FAILURE.value),
                e)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), result)),
                        200,
                        mimetype="application/json")

    @route('/file', methods=['GET'])
    def get_file(self):
        header_key = 'File-Path'

        file_path = request.headers.get(f"{header_key}")
        if not file_path:
            raise ApiExceptionDocker(
                ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)

        try:
            file_content = IOUtils.read_file(file_path)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.GET_FILE_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.GET_FILE_FAILURE.value), e)
        return Response(file_content, 200, mimetype="text/plain")

    @route('/file', methods=['POST', 'PUT'])
    def upload_file(self):
        http = HttpResponse()
        io_utils = IOUtils()
        header_key = 'File-Path'
        file_content = request.get_data()
        file_path = request.headers.get(f"{header_key}")

        if not file_path:
            raise ApiExceptionDocker(
                ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)
        if not file_content:
            raise ApiExceptionDocker(
                ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value),
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value))

        try:
            io_utils.write_to_file_binary(file_path, file_content)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.UPLOAD_FILE_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.UPLOAD_FILE_FAILURE.value),
                e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value))),
                        200,
                        mimetype="application/json")

    @route('/deployments/logs/<env_id>', methods=['GET'])
    def deploy_logs(self, env_id):
        env_id = env_id.lower()
        http = HttpResponse()
        docker_utils = DockerUtils()
        env_id = env_id.strip()
        env_id_dir = EnvInit.init.get(EnvConstants.DEPLOY_PATH) + f"/{env_id}"
        file = f"{env_id_dir}/docker-compose.yml"

        try:
            status = docker_utils.logs(file)
            app.logger.debug({"msg": status})
            if status.get('err'):
                raise Exception(status.get('err'))
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.GET_LOGS_FAILED.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.GET_LOGS_FAILED.value) %
                env_id, e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          status.get('out'))),
                        200,
                        mimetype="application/json")

    # must connect the container to the deployer network to be able to send http request
    @route('/deployments/network/<env_id>', methods=['POST', 'PUT'])
    def container_docker_network_connect(self, env_id):
        env_id = env_id.lower()
        http = HttpResponse()
        docker_utils = DockerUtils()
        headers = request.headers
        service_name = "container"

        if request.args.get('service') is not None:
            service_name = request.args.get('service')
        container_id = f"{env_id}_{service_name}_1"

        try:
            status = CmdUtils.run_cmd_shell_false([
                "docker", "network", "ls", "--filter",
                "name={}".format("deployer")
            ])
            app.logger.debug({"msg": status})
            if not status.get('out'):
                raise Exception(status.get('err'))
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.GET_DEPLOYER_NETWORK_FAILED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.GET_DEPLOYER_NETWORK_FAILED.value), e)

        try:
            deployer_network = status.get('out').split("\n")[1].split(
                " ")[0].strip()
            if headers.get("Docker-Network"):
                deployer_network = headers.get("Docker-Network")
            status = docker_utils.network_connect(deployer_network,
                                                  container_id)

            if "already exists in network".lower() in status.get(
                    'err').lower():
                return Response(json.dumps(
                    http.response(
                        ApiCode.SUCCESS.value,
                        ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                        "Success, already connected: " + status.get('err'))),
                                200,
                                mimetype="application/json")

            if "Error response from daemon".lower() in status.get(
                    'err').lower():
                raise Exception(status.get('err'))
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.CONTAINER_NET_CONNECT_FAILED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.CONTAINER_NET_CONNECT_FAILED.value), e)
        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          status.get('out'))),
                        200,
                        mimetype="application/json")

    @route('/deployments/network/<env_id>', methods=['DELETE'])
    def container_docker_network_disconnect(self, env_id):
        env_id = env_id.lower()
        http = HttpResponse()
        docker_utils = DockerUtils()
        service_name = "container"
        if request.args.get('service') is not None:
            service_name = request.args.get('service')
        container_id = f"{env_id}_{service_name}_1"

        try:
            status = CmdUtils.run_cmd_shell_false([
                "docker", "network", "ls", "--filter",
                "name={}".format("deployer")
            ])
            app.logger.debug({"msg": status})
            if not status.get('out'):
                raise Exception(status.get('err'))
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.GET_DEPLOYER_NETWORK_FAILED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.GET_DEPLOYER_NETWORK_FAILED.value), e)

        try:
            deployer_network = status.get('out').split("\n")[1].split(
                " ")[0].strip()
            status = docker_utils.network_disconnect(deployer_network,
                                                     container_id)

            if "is not connected to network".lower() in status.get(
                    'err').lower():
                return Response(json.dumps(
                    http.response(
                        ApiCode.SUCCESS.value,
                        ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                        "Success, already disconnected: " +
                        status.get('err'))),
                                200,
                                mimetype="application/json")

            if "Error response from daemon".lower() in status.get(
                    'err').lower():
                raise Exception(status.get('err'))
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.CONTAINER_NET_DISCONNECT_FAILED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.CONTAINER_NET_DISCONNECT_FAILED.value), e)
        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          status.get('out'))),
                        200,
                        mimetype="application/json")

    # here the requests are redirected to any container
    # url format: container/ENV_ID/the_url?port=8080&service=container
    # E.g.1 /container/2a1c9aa0451add84/uploadtestconfig
    @route('/container/<env_id>/<path:text>',
           methods=[
               'GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS',
               'TRACE', 'PATCH'
           ])
    def container_request(self, env_id, text):
        env_id = env_id.lower()
        container_url = text.strip()
        headers = request.headers

        service_name = "container"
        if request.args.get('service') is not None:
            service_name = request.args.get('service')
        container_id = f"{env_id}_{service_name}_1"

        container_port = 8080
        if request.args.get('port') is not None:
            container_port = request.args.get('port')

        input_data = None
        try:
            input_data = request.get_data()
        except Exception as e:
            app.logger.debug(
                f"Could not get data from the request: {e.__str__}")

        complete_url = f"http://{container_id}:{container_port}/{container_url}"
        try:
            r = requests.request(url=complete_url,
                                 method=request.method,
                                 data=input_data,
                                 headers=headers,
                                 timeout=5)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.CONTAINER_UNREACHABLE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.CONTAINER_UNREACHABLE.value)
                % (service_name, service_name), e)
        return Response(r.text, r.status_code)

    @route('/command', methods=['POST', 'PUT'])
    def execute_command(self):
        input_data = request.data.decode("UTF-8", "replace").strip()

        if not input_data:
            raise ApiExceptionDocker(
                ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value),
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value))
        try:
            input_data_list = input_data.split("\n")
            input_data_list = list(map(lambda x: x.strip(), input_data_list))
            command_in_memory = CommandInMemory()
            response = command_in_memory.run_commands(input_data_list)
        except Exception as e:
            raise ApiExceptionDocker(
                ApiCode.COMMAND_EXEC_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.COMMAND_EXEC_FAILURE.value),
                e)
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), response)),
                        200,
                        mimetype="application/json")
예제 #7
0
    def start_deployment(self):
        docker_utils = DockerUtils()
        token = token_hex(8)
        deployment_id = request.headers.get("Deployment-Id").lower(
        ) if request.headers.get("Deployment-Id") else token
        deploy_dir = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{deployment_id}"
        file = f"{deploy_dir}/docker-compose.yml"
        header_key = 'Eureka-Server'
        eureka_server_header = request.headers.get(f"{header_key}")
        config_env_vars = EnvStartupSingleton.get_instance(
        ).get_config_env_vars()
        input_data = request.data.decode('UTF-8').strip()

        status = CmdUtils.run_cmd_shell_false(["docker", "ps"])
        if "Cannot connect to the Docker daemon".lower() in status.get(
                'err').lower():
            raise ApiExceptionDocker(
                ApiCode.DOCKER_DAEMON_NOT_RUNNING.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.DOCKER_DAEMON_NOT_RUNNING.value),
                status.get('err'))

        active_deployments = docker_utils.get_active_deployments()
        if len(active_deployments) >= EnvInit.init.get(
                EnvConstants.MAX_DEPLOYMENTS):
            raise ApiExceptionDocker(
                ApiCode.MAX_DEPLOYMENTS_REACHED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.MAX_DEPLOYMENTS_REACHED.value) %
                str(EnvInit.init.get(EnvConstants.MAX_DEPLOYMENTS)),
                active_deployments)
        try:
            template_file_name = f"deployment_{deployment_id}.yml"
            template_file_path = f"{EnvInit.init.get(EnvConstants.TEMPLATES_DIR)}/{template_file_name}"
            app.logger.debug({
                "msg": {
                    "file": template_file_path,
                    "file_content": f"{input_data}"
                }
            })
            IOUtils.write_to_file(template_file_path, input_data)

            IOUtils.create_dir(deploy_dir)
            EnvironmentSingleton.get_instance().set_env_var(
                EnvConstants.TEMPLATE, template_file_name)
            env_vars = EnvironmentSingleton.get_instance(
            ).get_env_and_virtual_env()
            render = Render(env_vars.get(EnvConstants.TEMPLATE),
                            env_vars.get(EnvConstants.VARIABLES))
            if config_env_vars.get(
                    EnvConstants.EUREKA_SERVER) and config_env_vars.get(
                        EnvConstants.APP_IP_PORT):
                # if {{app_ip_port}} and {{eureka_server}} then register that instance too
                if '{{app_ip_port}}' in input_data and '{{eureka_server}}' in input_data:
                    eureka_server = config_env_vars.get(
                        EnvConstants.EUREKA_SERVER)
                    # header value overwrite the eureka server
                    if eureka_server_header:
                        eureka_server = eureka_server_header
                    input_data = render.get_jinja2env().get_template(
                        env_vars.get(EnvConstants.TEMPLATE)).render({
                            "deployment_id":
                            f"{deployment_id}",
                            "eureka_server":
                            eureka_server,
                            "app_ip_port":
                            config_env_vars.get(
                                EnvConstants.APP_IP_PORT).split("/")[0]
                        })
            os.remove(template_file_path) if os.path.exists(
                template_file_path) else None
            app.logger.debug(
                {"msg": {
                    "file": file,
                    "file_content": f"{input_data}"
                }})
            IOUtils.write_to_file(file, input_data) if input_data else None
            CmdUtils.run_cmd_detached(
                rf'''docker-compose -f {file} pull && docker-compose -f {file} up -d'''
            )
        except Exception as e:
            app.logger.debug({"msg": docker_utils.down(file)})
            raise ApiExceptionDocker(
                ApiCode.DEPLOY_START_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_START_FAILURE.value),
                e)

        DeploymentMetadataSingleton.get_instance() \
            .delete_metadata_for_inactive_deployments(DockerUtils.get_active_deployments())
        metadata = DeploymentReader.get_metadata_for_deployment(
            IOUtils.read_file(file=file))
        IOUtils.write_to_file_dict(f"{deploy_dir}/metadata.json", metadata)
        DeploymentMetadataSingleton.get_instance().set_metadata_for_deployment(
            deployment_id, metadata)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), deployment_id)),
                        200,
                        mimetype="application/json")
예제 #8
0
from rest.utils.env_startup import EnvStartupSingleton
from rest.utils.io_utils import IOUtils

DockerView.register(app=app)
KubectlView.register(app=app)

app.register_error_handler(ApiExceptionDocker, DockerView.handle_api_error)
app.register_error_handler(ApiExceptionKubectl, KubectlView.handle_api_error)

if __name__ == "__main__":
    cli = sys.modules['flask.cli']
    cli.show_server_banner = lambda *x: None

    fluentd_tag = "startup"
    host = '0.0.0.0'
    port = EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.PORT)
    message_dumper = MessageDumper()
    io_utils = IOUtils()

    if EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.EUREKA_SERVER):
        Eureka(EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.EUREKA_SERVER)).register_app(
            EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.APP_IP_PORT),
            EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.APP_APPEND_LABEL))

    io_utils.create_dirs([Path(EnvInit.init.get(EnvConstants.DEPLOY_PATH)),
                          Path(EnvInit.init.get(EnvConstants.TEMPLATES_DIR)),
                          Path(EnvInit.init.get(EnvConstants.VARS_DIR))])

    DockerEnvExpireScheduler(fluentd_utils=DockerView.fluentd,
                             poll_interval=EnvStartupSingleton.get_instance().get_config_env_vars().get(
                                 EnvConstants.SCHEDULER_POLL_INTERVAL),  # seconds
예제 #9
0
    def __emit(self, tag, msg):
        if EnvStartupSingleton.get_instance().get_config_env_vars().get(
                EnvConstants.FLUENTD_IP_PORT):
            return str(self.logger.emit(tag, msg)).lower()

        return "fluentd logging not enabled"
예제 #10
0
#!/usr/bin/env python3
import sys

from rest.api.constants.env_constants import EnvConstants
from rest.api.eureka_registrator import EurekaRegistrator
from rest.api.loghelpers.message_dumper import MessageDumper
from rest.api.routes import app, fluentd_service
from rest.environment.environment import EnvironmentSingleton
from rest.utils.env_startup import EnvStartupSingleton

if __name__ == "__main__":
    cli = sys.modules['flask.cli']
    cli.show_server_banner = lambda *x: None

    port = EnvStartupSingleton.get_instance().get_config_env_vars().get(
        EnvConstants.PORT)
    message_dumper = MessageDumper()
    host = '0.0.0.0'
    fluentd_tag = "startup"

    if EnvStartupSingleton.get_instance().get_config_env_vars().get(
            EnvConstants.EUREKA_SERVER):
        EurekaRegistrator(EnvStartupSingleton.get_instance(
        ).get_config_env_vars().get(EnvConstants.EUREKA_SERVER)).register_app(
            EnvStartupSingleton.get_instance().get_config_env_vars().get(
                EnvConstants.APP_IP),
            EnvStartupSingleton.get_instance().get_config_env_vars().get(
                EnvConstants.PORT))

    environ_dump = message_dumper.dump_message(
        EnvironmentSingleton.get_instance().get_env_and_virtual_env())
예제 #11
0
def agents_request(text):
    path = text.strip()
    eureka = Eureka(
        EnvStartupSingleton.get_instance().get_config_env_vars().get(
            EnvConstants.EUREKA_SERVER))
    ip_addr_port_header_key = 'IpAddr-Port'  # target specific agent
    home_page_url_header_key = 'HomePageUrl'  # target specific agent
    application = "agent"
    try:
        input_data = request.get_data()
    except:
        input_data = ""

    try:
        request_object = {
            "uri": path.lstrip("/"),
            "method": request.method,
            "headers": request.headers,
            "data": input_data
        }
        app.logger.debug({"msg": f"{request_object}"})
        agent_apps = eureka.get_type_eureka_apps(application)

        if request.headers.get(f"{ip_addr_port_header_key}"):  # not mandatory
            ip_port_list = request.headers.get(
                f"{ip_addr_port_header_key}").split(",")
            agent_apps = [
                app for app in agent_apps
                if f"{app.get('ipAddr')}:{app.get('port')}" in ip_port_list
            ]

        if request.headers.get(f"{home_page_url_header_key}"):  # not mandatory
            home_page_url_list = request.headers.get(
                f"{home_page_url_header_key}").split(",")
            agent_apps = [
                app for app in agent_apps
                if f"{app.get('homePageUrl')}" in home_page_url_list
            ]

        th_utils = ThreadUtils(apps=agent_apps, headers={})
        th_utils.spawn_threads_send_request(request_object)

    except Exception as e:
        raise ApiException(
            ApiCode.DISCOVERY_ERROR.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.DISCOVERY_ERROR.value), e)

    threads_response = th_utils.get_threads_response()

    if th_utils.is_response_zip():
        folder_path = f"{th_utils.download_folder}/{th_utils.get_source_zip_folder()}"
        archive_path = f"{th_utils.download_folder}/{th_utils.get_source_zip_folder()}"
        try:
            IOUtils.zip_file(archive_path, folder_path)
        except Exception as e:
            raise ApiException(
                ApiCode.FOLDER_ZIP_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.FOLDER_ZIP_FAILURE.value) %
                folder_path, e)
        IOUtils.delete_file(folder_path)

        return flask.send_file(f"{archive_path}.zip",
                               mimetype='application/zip',
                               as_attachment=True), 200

    return Response(json.dumps(HttpResponse().response(
        ApiCode.SUCCESS.value,
        ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), threads_response)),
                    200,
                    mimetype="application/json")
예제 #12
0
from rest.api.loghelpers.message_dumper import MessageDumper
from rest.api.responsehelpers.error_codes import ErrorMessage
from rest.api.responsehelpers.http_response import HttpResponse
from rest.environment.environment import EnvironmentSingleton
from rest.service.eureka import Eureka
from rest.service.fluentd import Fluentd
from rest.utils.env_startup import EnvStartupSingleton
from rest.utils.io_utils import IOUtils
from rest.utils.thread_utils import ThreadUtils

app = AppCreatorSingleton.get_instance().get_app()
auth = HTTPBasicAuth()

logger = \
    sender.FluentSender(tag=properties.get('name'),
                        host=EnvStartupSingleton.get_instance().get_config_env_vars().get(
                            EnvConstants.FLUENTD_IP_PORT).split(":")[0],
                        port=int(EnvStartupSingleton.get_instance().get_config_env_vars().get(
                            EnvConstants.FLUENTD_IP_PORT).split(":")[1])) \
        if EnvStartupSingleton.get_instance().get_config_env_vars().get(
        EnvConstants.FLUENTD_IP_PORT) else None
fluentd_service = Fluentd(logger)
message_dumper = MessageDumper()
env = EnvironmentSingleton.get_instance()

users = {
    EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.HTTP_AUTH_USER):
    generate_password_hash(
        EnvStartupSingleton.get_instance().get_config_env_vars().get(
            EnvConstants.HTTP_AUTH_PASSWORD))
}
예제 #13
0
class KubectlView(FlaskView):
    logger = \
        sender.FluentSender(tag=properties.get('name'),
                            host=EnvStartupSingleton.get_instance().get_config_env_vars().get(
                                EnvConstants.FLUENTD_IP_PORT).split(":")[0],
                            port=int(EnvStartupSingleton.get_instance().get_config_env_vars().get(
                                EnvConstants.FLUENTD_IP_PORT).split(":")[1])) \
            if EnvStartupSingleton.get_instance().get_config_env_vars().get(EnvConstants.FLUENTD_IP_PORT) else None
    fluentd = Fluentd(logger)
    message_dumper = MessageDumper()

    def before_request(self, name, *args, **kwargs):
        ctx = app.app_context()
        ctx.g.xid = token_hex(8)
        http = HttpResponse()
        request_uri = request.full_path
        # add here your custom header to be logged with fluentd
        self.message_dumper.set_header(
            HeaderConstants.X_REQUEST_ID,
            request.headers.get(HeaderConstants.X_REQUEST_ID) if
            request.headers.get(HeaderConstants.X_REQUEST_ID) else ctx.g.xid)
        self.message_dumper.set_header(HeaderConstants.REQUEST_URI,
                                       request_uri)

        response = self.fluentd.emit(
            tag="api", msg=self.message_dumper.dump(request=request))
        app.logger.debug(f"{response}")
        if not str(request.headers.get(HeaderConstants.TOKEN)) == str(
                EnvStartupSingleton.get_instance().get_config_env_vars().get(
                    EnvConstants.HTTP_AUTH_TOKEN)):
            if not ("/apidocs" in request_uri or "/swagger/swagger.json"
                    in request_uri):  # exclude swagger
                headers = {
                    HeaderConstants.X_REQUEST_ID:
                    self.message_dumper.get_header(
                        HeaderConstants.X_REQUEST_ID)
                }
                return Response(json.dumps(
                    http.response(
                        ApiCode.UNAUTHORIZED.value,
                        ErrorMessage.HTTP_CODE.get(ApiCode.UNAUTHORIZED.value),
                        "Invalid Token")),
                                401,
                                mimetype="application/json",
                                headers=headers)

    def after_request(self, name, http_response):
        headers = dict(http_response.headers)
        headers[HeaderConstants.X_REQUEST_ID] = self.message_dumper.get_header(
            HeaderConstants.X_REQUEST_ID)
        http_response.headers = headers

        http_response.direct_passthrough = False
        response = self.fluentd.emit(
            tag="api", msg=self.message_dumper.dump(http_response))
        app.logger.debug(f"{response}")

        return http_response

    @classmethod
    def handle_api_error(cls, e):
        http_response = Response(json.dumps(HttpResponse().response(
            code=e.code,
            message=e.message,
            description="Exception({})".format(e.exception.__str__()))),
                                 500,
                                 mimetype="application/json")
        http_response.headers[
            HeaderConstants.
            X_REQUEST_ID] = KubectlView.message_dumper.get_header(
                HeaderConstants.X_REQUEST_ID)
        response = KubectlView.fluentd.emit(
            tag="api", msg=KubectlView.message_dumper.dump(http_response))
        app.logger.debug(f"{response}")

        http_response.direct_passthrough = False
        return http_response

    def index(self):
        return render_template('index.html')

    @route('/apidocs')
    def send_swagger(self):
        return render_template('index.html')

    @route('/resources/<path:path>')
    def send_resources(self, path):
        return send_from_directory('swaggerui/resources', path)

    @route('/swagger/swagger.json')
    def swagger(self):
        return render_template('swagger_kubectl.json')

    @route('/ping')
    def ping(self):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), "pong")),
                        200,
                        mimetype="application/json")

    @route('/about')
    def about(self):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), about_system)),
                        200,
                        mimetype="application/json")

    @route('/env', methods=['GET'])
    def get_env_vars(self):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            EnvironmentSingleton.get_instance().get_env_and_virtual_env())),
                        200,
                        mimetype="application/json")

    @route('/env/<env_var>', methods=['GET'])
    def get_env_var(self, env_var):
        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
            EnvironmentSingleton.get_instance().get_env_and_virtual_env().get(
                env_var))),
                        200,
                        mimetype="application/json")

    @route('/env', methods=['POST'])
    def set_env(self):
        http = HttpResponse()
        input_data = request.data.decode("UTF-8", "replace").strip()

        try:
            env_vars_attempted = json.loads(input_data)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.INVALID_JSON_PAYLOAD.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.INVALID_JSON_PAYLOAD.value)
                % str(input_data), e)

        try:
            env_vars_added = EnvironmentSingleton.get_instance().set_env_vars(
                env_vars_attempted)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.SET_ENV_VAR_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.SET_ENV_VAR_FAILURE.value) %
                str(input_data), e)
        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          env_vars_added)),
                        200,
                        mimetype="application/json")

    @route('/render/<template>/<variables>', methods=['GET', 'POST'])
    def get_content_with_env(self, template, variables):
        try:
            input_json = request.get_json(force=True)
            for key, value in input_json.items():
                if key not in EnvironmentSingleton.get_instance().get_env():
                    EnvironmentSingleton.get_instance().set_env_var(
                        str(key), str(value))
        except Exception as e:
            app.logger.debug(f"Exception: {e.__str__}")

        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.TEMPLATE, template.strip())
        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.VARIABLES, variables.strip())

        try:
            rendered_content = Render(
                EnvironmentSingleton.get_instance().get_env_and_virtual_env(
                ).get(EnvConstants.TEMPLATE),
                EnvironmentSingleton.get_instance().get_env_and_virtual_env().
                get(EnvConstants.VARIABLES)).rend_template()
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.JINJA2_RENDER_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.JINJA2_RENDER_FAILURE.value), e)

        return Response(rendered_content, 200, mimetype="text/plain")

    @route('/deployments', methods=['GET'])
    def get_deployment_info(self):
        http = HttpResponse()
        kubectl_utils = KubectlUtils()
        header_keys = ["Label-Selector", 'K8s-Namespace']

        for header_key in header_keys:
            if not request.headers.get(f"{header_key}"):
                raise ApiExceptionKubectl(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                    ErrorMessage.HTTP_CODE.get(
                        ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                    ErrorMessage.HTTP_CODE.get(
                        ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)
        label_selector = request.headers.get(f"{header_keys[0]}")
        namespace = request.headers.get(f"{header_keys[1]}")
        active_pods = kubectl_utils.get_active_pods(label_selector, namespace)
        app.logger.debug(
            {"msg": {
                "active_deployments": f"{len(active_pods)}"
            }})
        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          active_pods)),
                        200,
                        mimetype="application/json")

    @route('/deployments', methods=['POST'])
    def deploy_start(self):
        http = HttpResponse()
        kubectl_utils = KubectlUtils()
        fluentd_tag = "deploy_start"
        token = token_hex(8)
        deploy_dir = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{token}"
        file = f"{deploy_dir}/k8s-deployment.yml"

        try:
            IOUtils.create_dir(deploy_dir)
            input_data = request.data.decode('utf-8')
            IOUtils.write_to_file(file, input_data)
            status = kubectl_utils.up(f"{file}")
            self.fluentd.emit(tag=fluentd_tag, msg={"msg": status})
            if status.get('err'):
                raise Exception(status.get('err'))
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.DEPLOY_START_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_START_FAILURE.value),
                e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          token)),
                        200,
                        mimetype="application/json")

    @route('/deployments/<template>/<variables>', methods=['POST'])
    def start_deployment_with_template(self, template, variables):
        http = HttpResponse()
        kubectl_utils = KubectlUtils()
        fluentd_tag = "deploy_start"
        try:
            input_json = request.get_json(force=True)
            for key, value in input_json.items():
                if key not in EnvironmentSingleton.get_instance().get_env():
                    EnvironmentSingleton.get_instance().set_env_var(
                        str(key), str(value))
        except Exception as e:
            app.logger.debug(f"Exception: {e.__str__()}")

        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.TEMPLATE, template.strip())
        EnvironmentSingleton.get_instance().set_env_var(
            EnvConstants.VARIABLES, variables.strip())
        app.logger.debug({
            "msg": {
                "template_file":
                EnvironmentSingleton.get_instance().get_env_and_virtual_env().
                get(EnvConstants.TEMPLATE)
            }
        })
        app.logger.debug({
            "msg": {
                "variables_file":
                EnvironmentSingleton.get_instance().get_env_and_virtual_env().
                get(EnvConstants.VARIABLES)
            }
        })
        token = token_hex(8)
        deploy_dir = f"{EnvInit.init.get(EnvConstants.DEPLOY_PATH)}/{token}"
        file = f"{deploy_dir}/k8s-deployment.yml"

        try:
            r = Render(
                EnvironmentSingleton.get_instance().get_env_and_virtual_env(
                ).get(EnvConstants.TEMPLATE),
                EnvironmentSingleton.get_instance().get_env_and_virtual_env().
                get(EnvConstants.VARIABLES))
            IOUtils.create_dir(deploy_dir)
            IOUtils.write_to_file(file)
            IOUtils.write_to_file(file, r.rend_template())
            status = kubectl_utils.up(f"{file}")
            self.fluentd.emit(tag=fluentd_tag, msg={"msg": status})
            if status.get('err'):
                raise Exception(status.get('error'))
            result = str(token)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.DEPLOY_START_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_START_FAILURE.value),
                e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          result)),
                        200,
                        mimetype="application/json")

    @route('/deployments/<deployment>', methods=['DELETE'])
    def delete_deployment(self, deployment):
        http = HttpResponse()
        deployment = deployment.strip()
        kubectl_utils = KubectlUtils()
        header_key = 'K8s-Namespace'
        fluentd_tag = "deploy_stop"

        if not request.headers.get(f"{header_key}"):
            raise ApiExceptionKubectl(
                ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)

        try:
            namespace = request.headers.get(f"{header_key}")
            status = kubectl_utils.down(deployment, namespace)
            self.fluentd.emit(tag=fluentd_tag, msg={"msg": status})
            if "Error".lower() in status.get('err').lower():
                raise Exception(status.get('err'))
            result = status.get('out').split("\n")[1:]
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.DEPLOY_STOP_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.DEPLOY_STOP_FAILURE.value),
                e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          result)),
                        200,
                        mimetype="application/json")

    @route('/deployments/<pod>', methods=['GET'])
    def get_deployment_status(self, pod):
        http = HttpResponse()
        pod = pod.strip()
        kubectl_utils = KubectlUtils()
        header_keys = ["Label-Selector", 'K8s-Namespace']

        for header_key in header_keys:
            if not request.headers.get(f"{header_key}"):
                raise ApiExceptionKubectl(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                    ErrorMessage.HTTP_CODE.get(
                        ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                    ErrorMessage.HTTP_CODE.get(
                        ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)

        try:
            label_selector = request.headers.get(f"{header_keys[0]}")
            namespace = request.headers.get(f"{header_keys[1]}")
            deployment = kubectl_utils.get_active_pod(pod, label_selector,
                                                      namespace)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.DEPLOY_STATUS_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.DEPLOY_STATUS_FAILURE.value), e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          deployment)),
                        200,
                        mimetype="application/json")

    @route('/file', methods=['POST', 'PUT'])
    def upload_file(self):
        http = HttpResponse()
        io_utils = IOUtils()
        header_key = 'File-Path'
        file_content = request.get_data()
        file_path = request.headers.get(f"{header_key}")
        if not file_path:
            raise ApiExceptionKubectl(
                ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)

        if not file_content:
            raise ApiExceptionKubectl(
                ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value),
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value))

        try:
            io_utils.write_to_file_binary(file_path, file_content)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.UPLOAD_FILE_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.UPLOAD_FILE_FAILURE.value),
                e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value))),
                        200,
                        mimetype="application/json")

    @route('/file', methods=['GET'])
    def get_file(self):
        header_key = 'File-Path'

        file_path = request.headers.get(f"{header_key}")
        if not file_path:
            raise ApiExceptionKubectl(
                ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)
        try:
            file_content = IOUtils.read_file(file_path)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.GET_FILE_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.GET_FILE_FAILURE.value), e)
        return Response(file_content, 200, mimetype="text/plain")

    @route('/deployments/logs/<deployment>', methods=['GET'])
    def deploy_logs(self, deployment):
        http = HttpResponse()
        kubectl_utils = KubectlUtils()
        deployment = deployment.strip()
        header_key = 'K8s-Namespace'

        if not request.headers.get(f"{header_key}"):
            raise ApiExceptionKubectl(
                ApiCode.HTTP_HEADER_NOT_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.HTTP_HEADER_NOT_PROVIDED.value) % header_key)

        try:
            if request.headers.get(f"{header_key}"):
                namespace = request.headers.get(f"{header_key}")
            status = kubectl_utils.logs(deployment, namespace)
            if status.get('err'):
                raise Exception(status)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.GET_LOGS_FAILED.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.GET_LOGS_FAILED.value) %
                deployment, e)

        return Response(json.dumps(
            http.response(ApiCode.SUCCESS.value,
                          ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value),
                          status.get('out'))),
                        200,
                        mimetype="application/json")

    @route('/command', methods=['POST', 'PUT'])
    def execute_command(self):
        input_data = request.data.decode("UTF-8", "replace").strip()

        if not input_data:
            raise ApiExceptionKubectl(
                ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value,
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value),
                ErrorMessage.HTTP_CODE.get(
                    ApiCode.EMPTY_REQUEST_BODY_PROVIDED.value))
        try:
            input_data_list = input_data.split("\n")
            input_data_list = list(map(lambda x: x.strip(), input_data_list))
            command_in_memory = CommandInMemory()
            response = command_in_memory.run_commands(input_data_list)
        except Exception as e:
            raise ApiExceptionKubectl(
                ApiCode.COMMAND_EXEC_FAILURE.value,
                ErrorMessage.HTTP_CODE.get(ApiCode.COMMAND_EXEC_FAILURE.value),
                e)

        return Response(json.dumps(HttpResponse().response(
            ApiCode.SUCCESS.value,
            ErrorMessage.HTTP_CODE.get(ApiCode.SUCCESS.value), response)),
                        200,
                        mimetype="application/json")