prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  kubernetes probes using HealthHandler endpoint succeed when models are not ready
  /kind bug
  
  **What steps did you take and what happened:**
  Used the [HealthHandler endpoint](https://github.com/kubeflow/kfserving/blob/92c5f3c9df662c853beef86bddf7f30ab5d8b996/python/kfserving/kfserving/kfserver.py#L62-L63) to set up a Kubernetes readiness probe. The probe succeeded even though the model was not ready. 
  
  **What did you expect to happen:**
  Readiness probe failure when model is not ready. 
  
  **Anything else you would like to add:**
  [Kubernetes probe documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) says `Any code greater than or equal to 200 and less than 400 indicates success. Any other code indicates failure`, which seems to imply that the [response contents](https://github.com/kubeflow/kfserving/blob/92c5f3c9df662c853beef86bddf7f30ab5d8b996/python/kfserving/kfserving/kfserver.py#L108-L111) (which include the model's `.ready` property) are ignored. 
  
  
  **Environment:**
  
  - Istio Version:
  - Knative Version:
  - KFServing Version:
  - Kubeflow version:
  - Minikube version:
  - Kubernetes version: (use `kubectl version`):
  - OS (e.g. from `/etc/os-release`):
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `python/kfserving/kfserving/kfserver.py`
  Content:
  ```
  1 # Copyright 2020 kubeflow.org.
  2 #
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 #
  7 #    http://www.apache.org/licenses/LICENSE-2.0
  8 #
  9 # Unless required by applicable law or agreed to in writing, software
  10 # distributed under the License is distributed on an "AS IS" BASIS,
  11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 # See the License for the specific language governing permissions and
  13 # limitations under the License.
  14 
  15 import argparse
  16 import logging
  17 import json
  18 from typing import List, Dict
  19 import tornado.ioloop
  20 import tornado.web
  21 import tornado.httpserver
  22 import tornado.log
  23 from kfserving.handlers.http import PredictHandler, ExplainHandler
  24 from kfserving import KFModel
  25 
  26 DEFAULT_HTTP_PORT = 8080
  27 DEFAULT_GRPC_PORT = 8081
  28 DEFAULT_MAX_BUFFER_SIZE = 104857600
  29 
  30 parser = argparse.ArgumentParser(add_help=False)
  31 parser.add_argument('--http_port', default=DEFAULT_HTTP_PORT, type=int,
  32                     help='The HTTP Port listened to by the model server.')
  33 parser.add_argument('--grpc_port', default=DEFAULT_GRPC_PORT, type=int,
  34                     help='The GRPC Port listened to by the model server.')
  35 parser.add_argument('--max_buffer_size', default=DEFAULT_MAX_BUFFER_SIZE, type=int,
  36                     help='The max buffer size for tornado.')
  37 parser.add_argument('--workers', default=0, type=int,
  38                     help='The number of works to fork')
  39 args, _ = parser.parse_known_args()
  40 
  41 tornado.log.enable_pretty_logging()
  42 
  43 class KFServer:
  44     def __init__(self, http_port: int = args.http_port,
  45                  grpc_port: int = args.grpc_port,
  46                  max_buffer_size: int = args.max_buffer_size,
  47                  workers: int = args.workers):
  48         self.registered_models = {}
  49         self.http_port = http_port
  50         self.grpc_port = grpc_port
  51         self.max_buffer_size = max_buffer_size
  52         self.workers = workers
  53         self._http_server = None
  54 
  55     def create_application(self):
  56         return tornado.web.Application([
  57             # Server Liveness API returns 200 if server is alive.
  58             (r"/", LivenessHandler),
  59             (r"/v1/models",
  60              ListHandler, dict(models=self.registered_models)),
  61             # Model Health API returns 200 if model is ready to serve.
  62             (r"/v1/models/([a-zA-Z0-9_-]+)",
  63              HealthHandler, dict(models=self.registered_models)),
  64             (r"/v1/models/([a-zA-Z0-9_-]+):predict",
  65              PredictHandler, dict(models=self.registered_models)),
  66             (r"/v1/models/([a-zA-Z0-9_-]+):explain",
  67              ExplainHandler, dict(models=self.registered_models)),
  68         ])
  69 
  70     def start(self, models: List[KFModel]):
  71         for model in models:
  72             self.register_model(model)
  73 
  74         self._http_server = tornado.httpserver.HTTPServer(
  75             self.create_application(), max_buffer_size=self.max_buffer_size)
  76 
  77         logging.info("Listening on port %s", self.http_port)
  78         self._http_server.bind(self.http_port)
  79         logging.info("Will fork %d workers", self.workers)
  80         self._http_server.start(self.workers)
  81         tornado.ioloop.IOLoop.current().start()
  82 
  83     def register_model(self, model: KFModel):
  84         if not model.name:
  85             raise Exception(
  86                 "Failed to register model, model.name must be provided.")
  87         self.registered_models[model.name] = model
  88         logging.info("Registering model: %s", model.name)
  89 
  90 
  91 class LivenessHandler(tornado.web.RequestHandler):  # pylint:disable=too-few-public-methods
  92     def get(self):
  93         self.write("Alive")
  94 
  95 
  96 class HealthHandler(tornado.web.RequestHandler):
  97     def initialize(self, models: Dict[str, KFModel]):
  98         self.models = models  # pylint:disable=attribute-defined-outside-init
  99 
  100     def get(self, name: str):
  101         if name not in self.models:
  102             raise tornado.web.HTTPError(
  103                 status_code=404,
  104                 reason="Model with name %s does not exist." % name
  105             )
  106 
  107         model = self.models[name]
  108         self.write(json.dumps({
  109             "name": model.name,
  110             "ready": model.ready
  111         }))
  112 
  113 
  114 class ListHandler(tornado.web.RequestHandler):
  115     def initialize(self, models: Dict[str, KFModel]):
  116         self.models = models  # pylint:disable=attribute-defined-outside-init
  117 
  118     def get(self):
  119         self.write(json.dumps(list(self.models.values())))
  120
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/python/kfserving/kfserving/kfserver.py b/python/kfserving/kfserving/kfserver.py
  --- a/python/kfserving/kfserving/kfserver.py
  +++ b/python/kfserving/kfserving/kfserver.py
  @@ -105,6 +105,12 @@
               )
   
           model = self.models[name]
  +        if not model.ready:
  +            raise tornado.web.HTTPError(
  +                status_code=503,
  +                reason="Model with name %s is not ready." % name
  +            )
  +
           self.write(json.dumps({
               "name": model.name,
               "ready": model.ready
  
verification_info: {"golden_diff": "diff --git a/python/kfserving/kfserving/kfserver.py b/python/kfserving/kfserving/kfserver.py\n--- a/python/kfserving/kfserving/kfserver.py\n+++ b/python/kfserving/kfserving/kfserver.py\n@@ -105,6 +105,12 @@\n             )\n \n         model = self.models[name]\n+        if not model.ready:\n+            raise tornado.web.HTTPError(\n+                status_code=503,\n+                reason=\"Model with name %s is not ready.\" % name\n+            )\n+\n         self.write(json.dumps({\n             \"name\": model.name,\n             \"ready\": model.ready\n", "issue": "kubernetes probes using HealthHandler endpoint succeed when models are not ready\n/kind bug\r\n\r\n**What steps did you take and what happened:**\r\nUsed the [HealthHandler endpoint](https://github.com/kubeflow/kfserving/blob/92c5f3c9df662c853beef86bddf7f30ab5d8b996/python/kfserving/kfserving/kfserver.py#L62-L63) to set up a Kubernetes readiness probe. The probe succeeded even though the model was not ready. \r\n\r\n**What did you expect to happen:**\r\nReadiness probe failure when model is not ready. \r\n\r\n**Anything else you would like to add:**\r\n[Kubernetes probe documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) says `Any code greater than or equal to 200 and less than 400 indicates success. Any other code indicates failure`, which seems to imply that the [response contents](https://github.com/kubeflow/kfserving/blob/92c5f3c9df662c853beef86bddf7f30ab5d8b996/python/kfserving/kfserving/kfserver.py#L108-L111) (which include the model's `.ready` property) are ignored. \r\n\r\n\r\n**Environment:**\r\n\r\n- Istio Version:\r\n- Knative Version:\r\n- KFServing Version:\r\n- Kubeflow version:\r\n- Minikube version:\r\n- Kubernetes version: (use `kubectl version`):\r\n- OS (e.g. from `/etc/os-release`):\r\n\n", "before_files": [{"content": "# Copyright 2020 kubeflow.org.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport logging\nimport json\nfrom typing import List, Dict\nimport tornado.ioloop\nimport tornado.web\nimport tornado.httpserver\nimport tornado.log\nfrom kfserving.handlers.http import PredictHandler, ExplainHandler\nfrom kfserving import KFModel\n\nDEFAULT_HTTP_PORT = 8080\nDEFAULT_GRPC_PORT = 8081\nDEFAULT_MAX_BUFFER_SIZE = 104857600\n\nparser = argparse.ArgumentParser(add_help=False)\nparser.add_argument('--http_port', default=DEFAULT_HTTP_PORT, type=int,\n                    help='The HTTP Port listened to by the model server.')\nparser.add_argument('--grpc_port', default=DEFAULT_GRPC_PORT, type=int,\n                    help='The GRPC Port listened to by the model server.')\nparser.add_argument('--max_buffer_size', default=DEFAULT_MAX_BUFFER_SIZE, type=int,\n                    help='The max buffer size for tornado.')\nparser.add_argument('--workers', default=0, type=int,\n                    help='The number of works to fork')\nargs, _ = parser.parse_known_args()\n\ntornado.log.enable_pretty_logging()\n\nclass KFServer:\n    def __init__(self, http_port: int = args.http_port,\n                 grpc_port: int = args.grpc_port,\n                 max_buffer_size: int = args.max_buffer_size,\n                 workers: int = args.workers):\n        self.registered_models = {}\n        self.http_port = http_port\n        self.grpc_port = grpc_port\n        self.max_buffer_size = max_buffer_size\n        self.workers = workers\n        self._http_server = None\n\n    def create_application(self):\n        return tornado.web.Application([\n            # Server Liveness API returns 200 if server is alive.\n            (r\"/\", LivenessHandler),\n            (r\"/v1/models\",\n             ListHandler, dict(models=self.registered_models)),\n            # Model Health API returns 200 if model is ready to serve.\n            (r\"/v1/models/([a-zA-Z0-9_-]+)\",\n             HealthHandler, dict(models=self.registered_models)),\n            (r\"/v1/models/([a-zA-Z0-9_-]+):predict\",\n             PredictHandler, dict(models=self.registered_models)),\n            (r\"/v1/models/([a-zA-Z0-9_-]+):explain\",\n             ExplainHandler, dict(models=self.registered_models)),\n        ])\n\n    def start(self, models: List[KFModel]):\n        for model in models:\n            self.register_model(model)\n\n        self._http_server = tornado.httpserver.HTTPServer(\n            self.create_application(), max_buffer_size=self.max_buffer_size)\n\n        logging.info(\"Listening on port %s\", self.http_port)\n        self._http_server.bind(self.http_port)\n        logging.info(\"Will fork %d workers\", self.workers)\n        self._http_server.start(self.workers)\n        tornado.ioloop.IOLoop.current().start()\n\n    def register_model(self, model: KFModel):\n        if not model.name:\n            raise Exception(\n                \"Failed to register model, model.name must be provided.\")\n        self.registered_models[model.name] = model\n        logging.info(\"Registering model: %s\", model.name)\n\n\nclass LivenessHandler(tornado.web.RequestHandler):  # pylint:disable=too-few-public-methods\n    def get(self):\n        self.write(\"Alive\")\n\n\nclass HealthHandler(tornado.web.RequestHandler):\n    def initialize(self, models: Dict[str, KFModel]):\n        self.models = models  # pylint:disable=attribute-defined-outside-init\n\n    def get(self, name: str):\n        if name not in self.models:\n            raise tornado.web.HTTPError(\n                status_code=404,\n                reason=\"Model with name %s does not exist.\" % name\n            )\n\n        model = self.models[name]\n        self.write(json.dumps({\n            \"name\": model.name,\n            \"ready\": model.ready\n        }))\n\n\nclass ListHandler(tornado.web.RequestHandler):\n    def initialize(self, models: Dict[str, KFModel]):\n        self.models = models  # pylint:disable=attribute-defined-outside-init\n\n    def get(self):\n        self.write(json.dumps(list(self.models.values())))\n", "path": "python/kfserving/kfserving/kfserver.py"}], "after_files": [{"content": "# Copyright 2020 kubeflow.org.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nimport argparse\nimport logging\nimport json\nfrom typing import List, Dict\nimport tornado.ioloop\nimport tornado.web\nimport tornado.httpserver\nimport tornado.log\nfrom kfserving.handlers.http import PredictHandler, ExplainHandler\nfrom kfserving import KFModel\n\nDEFAULT_HTTP_PORT = 8080\nDEFAULT_GRPC_PORT = 8081\nDEFAULT_MAX_BUFFER_SIZE = 104857600\n\nparser = argparse.ArgumentParser(add_help=False)\nparser.add_argument('--http_port', default=DEFAULT_HTTP_PORT, type=int,\n                    help='The HTTP Port listened to by the model server.')\nparser.add_argument('--grpc_port', default=DEFAULT_GRPC_PORT, type=int,\n                    help='The GRPC Port listened to by the model server.')\nparser.add_argument('--max_buffer_size', default=DEFAULT_MAX_BUFFER_SIZE, type=int,\n                    help='The max buffer size for tornado.')\nparser.add_argument('--workers', default=0, type=int,\n                    help='The number of works to fork')\nargs, _ = parser.parse_known_args()\n\ntornado.log.enable_pretty_logging()\n\nclass KFServer:\n    def __init__(self, http_port: int = args.http_port,\n                 grpc_port: int = args.grpc_port,\n                 max_buffer_size: int = args.max_buffer_size,\n                 workers: int = args.workers):\n        self.registered_models = {}\n        self.http_port = http_port\n        self.grpc_port = grpc_port\n        self.max_buffer_size = max_buffer_size\n        self.workers = workers\n        self._http_server = None\n\n    def create_application(self):\n        return tornado.web.Application([\n            # Server Liveness API returns 200 if server is alive.\n            (r\"/\", LivenessHandler),\n            (r\"/v1/models\",\n             ListHandler, dict(models=self.registered_models)),\n            # Model Health API returns 200 if model is ready to serve.\n            (r\"/v1/models/([a-zA-Z0-9_-]+)\",\n             HealthHandler, dict(models=self.registered_models)),\n            (r\"/v1/models/([a-zA-Z0-9_-]+):predict\",\n             PredictHandler, dict(models=self.registered_models)),\n            (r\"/v1/models/([a-zA-Z0-9_-]+):explain\",\n             ExplainHandler, dict(models=self.registered_models)),\n        ])\n\n    def start(self, models: List[KFModel]):\n        for model in models:\n            self.register_model(model)\n\n        self._http_server = tornado.httpserver.HTTPServer(\n            self.create_application(), max_buffer_size=self.max_buffer_size)\n\n        logging.info(\"Listening on port %s\", self.http_port)\n        self._http_server.bind(self.http_port)\n        logging.info(\"Will fork %d workers\", self.workers)\n        self._http_server.start(self.workers)\n        tornado.ioloop.IOLoop.current().start()\n\n    def register_model(self, model: KFModel):\n        if not model.name:\n            raise Exception(\n                \"Failed to register model, model.name must be provided.\")\n        self.registered_models[model.name] = model\n        logging.info(\"Registering model: %s\", model.name)\n\n\nclass LivenessHandler(tornado.web.RequestHandler):  # pylint:disable=too-few-public-methods\n    def get(self):\n        self.write(\"Alive\")\n\n\nclass HealthHandler(tornado.web.RequestHandler):\n    def initialize(self, models: Dict[str, KFModel]):\n        self.models = models  # pylint:disable=attribute-defined-outside-init\n\n    def get(self, name: str):\n        if name not in self.models:\n            raise tornado.web.HTTPError(\n                status_code=404,\n                reason=\"Model with name %s does not exist.\" % name\n            )\n\n        model = self.models[name]\n        if not model.ready:\n            raise tornado.web.HTTPError(\n                status_code=503,\n                reason=\"Model with name %s is not ready.\" % name\n            )\n\n        self.write(json.dumps({\n            \"name\": model.name,\n            \"ready\": model.ready\n        }))\n\n\nclass ListHandler(tornado.web.RequestHandler):\n    def initialize(self, models: Dict[str, KFModel]):\n        self.models = models  # pylint:disable=attribute-defined-outside-init\n\n    def get(self):\n        self.write(json.dumps(list(self.models.values())))\n", "path": "python/kfserving/kfserving/kfserver.py"}]}