AmmarFahmy
adding all files
105b369
from typing import Optional, Dict, List
from phi.app.db_app import DbApp
from phi.k8s.app.base import (
K8sApp,
AppVolumeType, # noqa: F401
ContainerContext,
ServiceType, # noqa: F401
RestartPolicy, # noqa: F401
ImagePullPolicy, # noqa: F401
)
from phi.utils.common import str_to_int
from phi.utils.log import logger
class SupersetBase(K8sApp):
# -*- App Name
name: str = "superset"
# -*- Image Configuration
image_name: str = "phidata/superset"
image_tag: str = "2.1.1"
# -*- App Ports
# Open a container port if open_port=True
open_port: bool = False
port_number: int = 8088
# -*- Python Configuration
# Set the PYTHONPATH env var
set_python_path: bool = True
# Add paths to the PYTHONPATH env var
add_python_paths: Optional[List[str]] = ["/app/pythonpath"]
# -*- Workspace Configuration
# Path to the parent directory of the workspace inside the container
# When using git-sync, the git repo is cloned inside this directory
# i.e. this is the parent directory of the workspace
workspace_parent_dir_container_path: str = "/usr/local/workspace"
# -*- Superset Configuration
# Set the SUPERSET_CONFIG_PATH env var
superset_config_path: Optional[str] = None
# Set the FLASK_ENV env var
flask_env: str = "production"
# Set the SUPERSET_ENV env var
superset_env: str = "production"
# -*- Superset Database Configuration
wait_for_db: bool = False
# Connect to the database using a DbApp
db_app: Optional[DbApp] = None
# Provide database connection details manually
# db_user can be provided here or as the
# DB_USER env var in the secrets_file
db_user: Optional[str] = None
# db_password can be provided here or as the
# DB_PASSWORD env var in the secrets_file
db_password: Optional[str] = None
# db_database can be provided here or as the
# DB_DATABASE env var in the secrets_file
db_database: Optional[str] = None
# db_host can be provided here or as the
# DB_HOST env var in the secrets_file
db_host: Optional[str] = None
# db_port can be provided here or as the
# DATABASE_PORT or DB_PORT env var in the secrets_file
db_port: Optional[int] = None
# db_driver can be provided here or as the
# DATABASE_DIALECT or DB_DRIVER env var in the secrets_file
db_driver: str = "postgresql+psycopg"
# -*- Superset Redis Configuration
wait_for_redis: bool = False
# Connect to redis using a DbApp
redis_app: Optional[DbApp] = None
# redis_host can be provided here or as the
# REDIS_HOST env var in the secrets_file
redis_host: Optional[str] = None
# redis_port can be provided here or as the
# REDIS_PORT env var in the secrets_file
redis_port: Optional[int] = None
# redis_driver can be provided here or as the
# REDIS_DRIVER env var in the secrets_file
redis_driver: Optional[str] = None
# -*- Other args
load_examples: bool = False
def get_db_user(self) -> Optional[str]:
return self.db_user or self.get_secret_from_file("DATABASE_USER") or self.get_secret_from_file("DB_USER")
def get_db_password(self) -> Optional[str]:
return (
self.db_password
or self.get_secret_from_file("DATABASE_PASSWORD")
or self.get_secret_from_file("DB_PASSWORD")
)
def get_db_database(self) -> Optional[str]:
return self.db_database or self.get_secret_from_file("DATABASE_DB") or self.get_secret_from_file("DB_DATABASE")
def get_db_driver(self) -> Optional[str]:
return self.db_driver or self.get_secret_from_file("DATABASE_DIALECT") or self.get_secret_from_file("DB_DRIVER")
def get_db_host(self) -> Optional[str]:
return self.db_host or self.get_secret_from_file("DATABASE_HOST") or self.get_secret_from_file("DB_HOST")
def get_db_port(self) -> Optional[int]:
return (
self.db_port
or str_to_int(self.get_secret_from_file("DATABASE_PORT"))
or str_to_int(self.get_secret_from_file("DB_PORT"))
)
def get_redis_host(self) -> Optional[str]:
return self.redis_host or self.get_secret_from_file("REDIS_HOST")
def get_redis_port(self) -> Optional[int]:
return self.redis_port or str_to_int(self.get_secret_from_file("REDIS_PORT"))
def get_redis_driver(self) -> Optional[str]:
return self.redis_driver or self.get_secret_from_file("REDIS_DRIVER")
def get_container_env(self, container_context: ContainerContext) -> Dict[str, str]:
from phi.constants import (
PHI_RUNTIME_ENV_VAR,
PYTHONPATH_ENV_VAR,
REQUIREMENTS_FILE_PATH_ENV_VAR,
SCRIPTS_DIR_ENV_VAR,
STORAGE_DIR_ENV_VAR,
WORKFLOWS_DIR_ENV_VAR,
WORKSPACE_DIR_ENV_VAR,
WORKSPACE_HASH_ENV_VAR,
WORKSPACE_ID_ENV_VAR,
WORKSPACE_ROOT_ENV_VAR,
)
# Container Environment
container_env: Dict[str, str] = self.container_env or {}
container_env.update(
{
"INSTALL_REQUIREMENTS": str(self.install_requirements),
"MOUNT_WORKSPACE": str(self.mount_workspace),
"PRINT_ENV_ON_LOAD": str(self.print_env_on_load),
PHI_RUNTIME_ENV_VAR: "kubernetes",
REQUIREMENTS_FILE_PATH_ENV_VAR: container_context.requirements_file or "",
SCRIPTS_DIR_ENV_VAR: container_context.scripts_dir or "",
STORAGE_DIR_ENV_VAR: container_context.storage_dir or "",
WORKFLOWS_DIR_ENV_VAR: container_context.workflows_dir or "",
WORKSPACE_DIR_ENV_VAR: container_context.workspace_dir or "",
WORKSPACE_ROOT_ENV_VAR: container_context.workspace_root or "",
"WAIT_FOR_DB": str(self.wait_for_db),
"WAIT_FOR_REDIS": str(self.wait_for_redis),
}
)
try:
if container_context.workspace_schema is not None:
if container_context.workspace_schema.id_workspace is not None:
container_env[WORKSPACE_ID_ENV_VAR] = str(container_context.workspace_schema.id_workspace) or ""
if container_context.workspace_schema.ws_hash is not None:
container_env[WORKSPACE_HASH_ENV_VAR] = container_context.workspace_schema.ws_hash
except Exception:
pass
if self.set_python_path:
python_path = self.python_path
if python_path is None:
python_path = container_context.workspace_root
if self.add_python_paths is not None:
python_path = "{}:{}".format(python_path, ":".join(self.add_python_paths))
if python_path is not None:
container_env[PYTHONPATH_ENV_VAR] = python_path
# Set aws region and profile
self.set_aws_env_vars(env_dict=container_env)
# Set the SUPERSET_CONFIG_PATH
if self.superset_config_path is not None:
container_env["SUPERSET_CONFIG_PATH"] = self.superset_config_path
# Set the FLASK_ENV
if self.flask_env is not None:
container_env["FLASK_ENV"] = self.flask_env
# Set the SUPERSET_ENV
if self.superset_env is not None:
container_env["SUPERSET_ENV"] = self.superset_env
# Set SUPERSET_LOAD_EXAMPLES
if self.load_examples is not None:
container_env["SUPERSET_LOAD_EXAMPLES"] = "yes"
# Set SUPERSET_PORT
if self.open_port and self.container_port is not None:
container_env["SUPERSET_PORT"] = str(self.container_port)
# Superset db connection
db_user = self.get_db_user()
db_password = self.get_db_password()
db_database = self.get_db_database()
db_host = self.get_db_host()
db_port = self.get_db_port()
db_driver = self.get_db_driver()
if self.db_app is not None and isinstance(self.db_app, DbApp):
logger.debug(f"Reading db connection details from: {self.db_app.name}")
if db_user is None:
db_user = self.db_app.get_db_user()
if db_password is None:
db_password = self.db_app.get_db_password()
if db_database is None:
db_database = self.db_app.get_db_database()
if db_host is None:
db_host = self.db_app.get_db_host()
if db_port is None:
db_port = self.db_app.get_db_port()
if db_driver is None:
db_driver = self.db_app.get_db_driver()
if db_user is not None:
container_env["DATABASE_USER"] = db_user
if db_host is not None:
container_env["DATABASE_HOST"] = db_host
if db_port is not None:
container_env["DATABASE_PORT"] = str(db_port)
if db_database is not None:
container_env["DATABASE_DB"] = db_database
if db_driver is not None:
container_env["DATABASE_DIALECT"] = db_driver
# Ideally we don't want the password in the env
# But the superset image expects it.
if db_password is not None:
container_env["DATABASE_PASSWORD"] = db_password
# Superset redis connection
redis_host = self.get_redis_host()
redis_port = self.get_redis_port()
redis_driver = self.get_redis_driver()
if self.redis_app is not None and isinstance(self.redis_app, DbApp):
logger.debug(f"Reading redis connection details from: {self.redis_app.name}")
if redis_host is None:
redis_host = self.redis_app.get_db_host()
if redis_port is None:
redis_port = self.redis_app.get_db_port()
if redis_driver is None:
redis_driver = self.redis_app.get_db_driver()
if redis_host is not None:
container_env["REDIS_HOST"] = redis_host
if redis_port is not None:
container_env["REDIS_PORT"] = str(redis_port)
if redis_driver is not None:
container_env["REDIS_DRIVER"] = str(redis_driver)
# Update the container env using env_file
env_data_from_file = self.get_env_file_data()
if env_data_from_file is not None:
container_env.update({k: str(v) for k, v in env_data_from_file.items() if v is not None})
# Update the container env with user provided env_vars
# this overwrites any existing variables with the same key
if self.env_vars is not None and isinstance(self.env_vars, dict):
container_env.update({k: str(v) for k, v in self.env_vars.items() if v is not None})
# logger.debug("Container Environment: {}".format(container_env))
return container_env