Skip to content

Recipes

zenml.recipes special

Recipes for ZenML stacks.

stack_recipe_service

Service for ZenML Stack Recipes.

StackRecipeService (TerraformService) pydantic-model

Class to represent terraform applications.

Source code in zenml/recipes/stack_recipe_service.py
class StackRecipeService(TerraformService):
    """Class to represent terraform applications."""

    SERVICE_TYPE = ServiceType(
        name="stackrecipes",
        description="Stack recipe service",
        type="terraform",
        flavor="recipes",
    )

    STACK_RECIPES_CONFIG_PATH: ClassVar[str] = os.path.join(
        io_utils.get_global_config_directory(),
        "stack_recipes",
    )

    # list of all enabled stack components
    enabled_services: List[str] = []

    def check_installation(self) -> None:
        """Checks if necessary tools are installed on the host system.

        Raises:
            RuntimeError: if any required tool is not installed.
        """
        super().check_installation()

        if not self._is_kubectl_installed():
            raise RuntimeError(
                "kubectl is not installed on your machine or not available on  "
                "your $PATH. It is used by stack recipes to create some "
                "resources on Kubernetes and to configure access to your "
                "cluster. Please visit "
                "https://kubernetes.io/docs/tasks/tools/#kubectl "
                "to install it."
            )
        if not self._is_helm_installed():
            raise RuntimeError(
                "Helm is not installed on your machine or not available on  "
                "your $PATH. It is required for stack recipes to create releases "
                "on Kubernetes. Please visit "
                "https://helm.sh/docs/intro/install/ "
                "to install it."
            )
        if not self._is_docker_installed():
            raise RuntimeError(
                "Docker is not installed on your machine or not available on  "
                "your $PATH. It is required for stack recipes to configure "
                "access to the container registry. Please visit "
                "https://docs.docker.com/engine/install/ "
                "to install it."
            )

    def _is_kubectl_installed(self) -> bool:
        """Checks if kubectl is installed on the host system.

        Returns:
            True if kubectl is installed, false otherwise.
        """
        try:
            subprocess.check_output(["kubectl"])
        except subprocess.CalledProcessError:
            return False

        return True

    def _is_helm_installed(self) -> bool:
        """Checks if helm is installed on the host system.

        Returns:
            True if helm is installed, false otherwise.
        """
        try:
            subprocess.check_output(["helm", "version"])
        except subprocess.CalledProcessError:
            return False

        return True

    def _is_docker_installed(self) -> bool:
        """Checks if docker is installed on the host system.

        Returns:
            True if docker is installed, false otherwise.
        """
        try:
            subprocess.check_output(["docker", "--version"])
        except subprocess.CalledProcessError:
            return False

        return True

    @property
    def stack_file_path(self) -> str:
        """Get the path to the stack yaml file.

        Returns:
            The path to the stack yaml file.
        """
        # return the path of the stack yaml file
        stack_file_path = self.terraform_client.output(
            STACK_FILE_NAME_OUTPUT, full_value=True
        )
        return str(stack_file_path)

    @classmethod
    def get_service(cls, recipe_path: str) -> Optional["StackRecipeService"]:
        """Load and return the stack recipe service, if present.

        Args:
            recipe_path: The path to the directory that hosts the recipe.

        Returns:
            The stack recipe service or None, if the stack recipe
            deployment is not found.
        """
        from zenml.services import ServiceRegistry

        try:
            for root, _, files in os.walk(str(cls.STACK_RECIPES_CONFIG_PATH)):
                for file in files:
                    if file == SERVICE_CONFIG_FILE_NAME:
                        service_config_path = os.path.join(root, file)
                        logger.debug(
                            "Loading service daemon configuration from %s",
                            service_config_path,
                        )
                        service_config = None
                        with open(service_config_path, "r") as f:
                            service_config = f.read()
                        stack_recipe_service = cast(
                            StackRecipeService,
                            ServiceRegistry().load_service_from_json(
                                service_config
                            ),
                        )
                        if (
                            stack_recipe_service.config.directory_path
                            == recipe_path
                        ):
                            return stack_recipe_service
            return None
        except FileNotFoundError:
            return None

    def get_vars(self) -> Dict[str, Any]:
        """Get variables as a dictionary.

        Returns:
            A dictionary of variables to use for the stack recipes
            derived from the tfvars.json file.
        """
        vars = super().get_vars()

        # enable services
        for service in self.enabled_services:
            vars[f"enable_{service}"] = True

        # update zenml version to current version
        vars[ZENML_VERSION_VARIABLE] = zenml.__version__
        return vars

    def get_deployment_info(self) -> str:
        """Return deployment details as a YAML document.

        Returns:
            A YAML document that can be passed as config to
            the server deploy function.
        """
        provider = yaml_utils.read_yaml(
            file_path=os.path.join(
                self.terraform_client.working_dir, "metadata.yaml"
            )
        )["Cloud"]

        config = {
            "name": f"{provider}",
            "provider": f"{provider}",
            "deploy_db": True,
            "create_ingress_controller": False,
            "ingress_controller_hostname": self.terraform_client.output(
                INGRESS_CONTROLLER_HOST_OUTPUT, full_value=True
            ),
        }

        if provider == "gcp":
            config["project_id"] = self.terraform_client.output(
                PROJECT_ID_OUTPUT, full_value=True
            )

        return cast(str, yaml.dump(config))
stack_file_path: str property readonly

Get the path to the stack yaml file.

Returns:

Type Description
str

The path to the stack yaml file.

check_installation(self)

Checks if necessary tools are installed on the host system.

Exceptions:

Type Description
RuntimeError

if any required tool is not installed.

Source code in zenml/recipes/stack_recipe_service.py
def check_installation(self) -> None:
    """Checks if necessary tools are installed on the host system.

    Raises:
        RuntimeError: if any required tool is not installed.
    """
    super().check_installation()

    if not self._is_kubectl_installed():
        raise RuntimeError(
            "kubectl is not installed on your machine or not available on  "
            "your $PATH. It is used by stack recipes to create some "
            "resources on Kubernetes and to configure access to your "
            "cluster. Please visit "
            "https://kubernetes.io/docs/tasks/tools/#kubectl "
            "to install it."
        )
    if not self._is_helm_installed():
        raise RuntimeError(
            "Helm is not installed on your machine or not available on  "
            "your $PATH. It is required for stack recipes to create releases "
            "on Kubernetes. Please visit "
            "https://helm.sh/docs/intro/install/ "
            "to install it."
        )
    if not self._is_docker_installed():
        raise RuntimeError(
            "Docker is not installed on your machine or not available on  "
            "your $PATH. It is required for stack recipes to configure "
            "access to the container registry. Please visit "
            "https://docs.docker.com/engine/install/ "
            "to install it."
        )
get_deployment_info(self)

Return deployment details as a YAML document.

Returns:

Type Description
str

A YAML document that can be passed as config to the server deploy function.

Source code in zenml/recipes/stack_recipe_service.py
def get_deployment_info(self) -> str:
    """Return deployment details as a YAML document.

    Returns:
        A YAML document that can be passed as config to
        the server deploy function.
    """
    provider = yaml_utils.read_yaml(
        file_path=os.path.join(
            self.terraform_client.working_dir, "metadata.yaml"
        )
    )["Cloud"]

    config = {
        "name": f"{provider}",
        "provider": f"{provider}",
        "deploy_db": True,
        "create_ingress_controller": False,
        "ingress_controller_hostname": self.terraform_client.output(
            INGRESS_CONTROLLER_HOST_OUTPUT, full_value=True
        ),
    }

    if provider == "gcp":
        config["project_id"] = self.terraform_client.output(
            PROJECT_ID_OUTPUT, full_value=True
        )

    return cast(str, yaml.dump(config))
get_service(recipe_path) classmethod

Load and return the stack recipe service, if present.

Parameters:

Name Type Description Default
recipe_path str

The path to the directory that hosts the recipe.

required

Returns:

Type Description
Optional[StackRecipeService]

The stack recipe service or None, if the stack recipe deployment is not found.

Source code in zenml/recipes/stack_recipe_service.py
@classmethod
def get_service(cls, recipe_path: str) -> Optional["StackRecipeService"]:
    """Load and return the stack recipe service, if present.

    Args:
        recipe_path: The path to the directory that hosts the recipe.

    Returns:
        The stack recipe service or None, if the stack recipe
        deployment is not found.
    """
    from zenml.services import ServiceRegistry

    try:
        for root, _, files in os.walk(str(cls.STACK_RECIPES_CONFIG_PATH)):
            for file in files:
                if file == SERVICE_CONFIG_FILE_NAME:
                    service_config_path = os.path.join(root, file)
                    logger.debug(
                        "Loading service daemon configuration from %s",
                        service_config_path,
                    )
                    service_config = None
                    with open(service_config_path, "r") as f:
                        service_config = f.read()
                    stack_recipe_service = cast(
                        StackRecipeService,
                        ServiceRegistry().load_service_from_json(
                            service_config
                        ),
                    )
                    if (
                        stack_recipe_service.config.directory_path
                        == recipe_path
                    ):
                        return stack_recipe_service
        return None
    except FileNotFoundError:
        return None
get_vars(self)

Get variables as a dictionary.

Returns:

Type Description
Dict[str, Any]

A dictionary of variables to use for the stack recipes derived from the tfvars.json file.

Source code in zenml/recipes/stack_recipe_service.py
def get_vars(self) -> Dict[str, Any]:
    """Get variables as a dictionary.

    Returns:
        A dictionary of variables to use for the stack recipes
        derived from the tfvars.json file.
    """
    vars = super().get_vars()

    # enable services
    for service in self.enabled_services:
        vars[f"enable_{service}"] = True

    # update zenml version to current version
    vars[ZENML_VERSION_VARIABLE] = zenml.__version__
    return vars