Repository: bentoml/BentoML

Issue:
bug: [bentoml-cli] `build` failed: Distro is required, got None instead.
### Describe the bug

when `bentoml build`, this error occurred. Today I moved to BentoML==1.0.11
it was fine with BentoML==1.0.10 when tried last.

### To reproduce

bentoml build

### Expected behavior

_No response_

### Environment

bentoml: 1.0.11
python 3.8


Discussion:
For more context, this is presumably occurring because we're using a custom docker base image. Our custom base image starts with `FROM python:3.8-slim`. Do we need to set an envvar or something to let BentoML know what distro this is?
`
I've tried specifying distro: "debian" in the bentofile.yaml, and that seems to have gotten me past this particular error (but I don't think this is a good long-term solution), but then I run into another issue that seems familiar:
```
bentoml.exceptions.InvalidArgument: Invalid build option: docker.python_version="None", python version must follow standard python semver format, e.g. 3.7.10 
```
This is a result of `docker.python_version` ending up as a string "None" (not `None`), and that causes the version validation to fail. I swear we previously solved for this issue, but I can't find where or how.

Found where that `python_version` issue was previously fixed: https://github.com/bentoml/BentoML/pull/2623/files
So yeah, this is a regression: https://github.com/bentoml/BentoML/commit/cd2d2ea35db07ac43b8bdfca9e62497c2c49b87d
<img width="489" alt="image" src="https://user-images.githubusercontent.com/1635487/206292942-e93d1173-a596-4e6a-8868-1201cc971d96.png">
By getting rid of those `t.Optional[str]` you've undone the fix from the PR I linked earlier, and that causes a build problem for users of a custom base image. @aarnphm 
I've confirmed that when I restore those `t.Optional` then I can get the bento to build. Note that I still have to provide `distro: "debian"` in order to get past the original issue reported here. That's undesirable (imo). Maybe we could use a value of "custom_docker_image" (or similar) for `DockerOptions.distro` when a custom docker image is used, and then we could add a dummy entry in `CONTAINER_METADATA`?

Resolving patch:
diff --git a/src/bentoml/_internal/bento/build_config.py b/src/bentoml/_internal/bento/build_config.py
--- a/src/bentoml/_internal/bento/build_config.py
+++ b/src/bentoml/_internal/bento/build_config.py
@@ -149,13 +149,13 @@ class DockerOptions:
     # always omit config values in case of default values got changed in future BentoML releases
     __omit_if_default__ = False
 
-    distro: str = attr.field(
+    distro: t.Optional[str] = attr.field(
         default=None,
         validator=attr.validators.optional(
             attr.validators.in_(CONTAINER_SUPPORTED_DISTROS)
         ),
     )
-    python_version: str = attr.field(
+    python_version: t.Optional[str] = attr.field(
         converter=_convert_python_version,
         default=None,
         validator=attr.validators.optional(
diff --git a/src/bentoml/_internal/container/generate.py b/src/bentoml/_internal/container/generate.py
--- a/src/bentoml/_internal/container/generate.py
+++ b/src/bentoml/_internal/container/generate.py
@@ -58,24 +58,27 @@ def get_templates_variables(
     """
     Returns a dictionary of variables to be used in BentoML base templates.
     """
-    spec = DistroSpec.from_options(docker, conda)
     conda_python_version = conda.get_python_version(bento_fs)
     if conda_python_version is None:
         conda_python_version = docker.python_version
 
-    python_version = docker.python_version
-    if docker.distro in ("ubi8"):
-        # ubi8 base images uses "py38" instead of "py3.8" in its image tag
-        python_version = python_version.replace(".", "")
-    base_image = spec.image.format(spec_version=python_version)
-    if docker.cuda_version is not None:
-        base_image = spec.image.format(spec_version=docker.cuda_version)
     if docker.base_image is not None:
         base_image = docker.base_image
         logger.info(
             "BentoML will not install Python to custom base images; ensure the base image '%s' has Python installed.",
             base_image,
         )
+    else:
+        spec = DistroSpec.from_options(docker, conda)
+        python_version = docker.python_version
+        assert docker.distro is not None and python_version is not None
+        if docker.distro in ("ubi8"):
+            # ubi8 base images uses "py38" instead of "py3.8" in its image tag
+            python_version = python_version.replace(".", "")
+        base_image = spec.image.format(spec_version=python_version)
+        if docker.cuda_version is not None:
+            base_image = spec.image.format(spec_version=docker.cuda_version)
+
     # bento__env
     default_env = {
         "uid_gid": BENTO_UID_GID,
diff --git a/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py b/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py
--- a/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py
+++ b/src/bentoml/_internal/server/grpc/servicer/v1/__init__.py
@@ -7,6 +7,7 @@
 from typing import TYPE_CHECKING
 
 import anyio
+
 from .....utils import LazyLoader
 from ......exceptions import InvalidArgument
 from ......exceptions import BentoMLException
@@ -22,6 +23,7 @@
 
     import grpc
     from google.protobuf import struct_pb2
+
     from ......grpc.v1 import service_pb2 as pb
     from ......grpc.v1 import service_pb2_grpc as services
     from ......grpc.types import BentoServicerContext


Test patch: