We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Support credentials for Azure Blobs
/kind feature

**Describe the solution you'd like**
1. Credentials builder should support exposing azure blob credentials to model initializer
2. Downloader should read and use credentials


**Anything else you would like to add:**
Here is how az creds are setup for kubeflow:  https://github.com/kubeflow/kubeflow/pull/2676

--- 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/storage.py`
Content:
```
1 # Copyright 2019 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 glob
16 import logging
17 import tempfile
18 import os
19 import re
20 from azure.storage.blob import BlockBlobService
21 from google.auth import exceptions
22 from google.cloud import storage
23 from minio import Minio
24 
25 _GCS_PREFIX = "gs://"
26 _S3_PREFIX = "s3://"
27 _BLOB_RE = "https://(.+?).blob.core.windows.net/(.+)"
28 _LOCAL_PREFIX = "file://"
29 
30 
31 class Storage(object): # pylint: disable=too-few-public-methods
32     @staticmethod
33     def download(uri: str, out_dir: str = None) -> str:
34         logging.info("Copying contents of %s to local", uri)
35 
36         is_local = False
37         if uri.startswith(_LOCAL_PREFIX) or os.path.exists(uri):
38             is_local = True
39 
40         if out_dir is None:
41             if is_local:
42                 # noop if out_dir is not set and the path is local
43                 return Storage._download_local(uri)
44             out_dir = tempfile.mkdtemp()
45 
46         if uri.startswith(_GCS_PREFIX):
47             Storage._download_gcs(uri, out_dir)
48         elif uri.startswith(_S3_PREFIX):
49             Storage._download_s3(uri, out_dir)
50         elif re.search(_BLOB_RE, uri):
51             Storage._download_blob(uri, out_dir)
52         elif is_local:
53             return Storage._download_local(uri, out_dir)
54         else:
55             raise Exception("Cannot recognize storage type for " + uri +
56                             "\n'%s', '%s', and '%s' are the current available storage type." %
57                             (_GCS_PREFIX, _S3_PREFIX, _LOCAL_PREFIX))
58 
59         logging.info("Successfully copied %s to %s", uri, out_dir)
60         return out_dir
61 
62     @staticmethod
63     def _download_s3(uri, temp_dir: str):
64         client = Storage._create_minio_client()
65         bucket_args = uri.replace(_S3_PREFIX, "", 1).split("/", 1)
66         bucket_name = bucket_args[0]
67         bucket_path = bucket_args[1] if len(bucket_args) > 1 else ""
68         objects = client.list_objects(bucket_name, prefix=bucket_path, recursive=True)
69         for obj in objects:
70             # Replace any prefix from the object key with temp_dir
71             subdir_object_key = obj.object_name.replace(bucket_path, "", 1).strip("/")
72             client.fget_object(bucket_name, obj.object_name,
73                                os.path.join(temp_dir, subdir_object_key))
74 
75     @staticmethod
76     def _download_gcs(uri, temp_dir: str):
77         try:
78             storage_client = storage.Client()
79         except exceptions.DefaultCredentialsError:
80             storage_client = storage.Client.create_anonymous_client()
81         bucket_args = uri.replace(_GCS_PREFIX, "", 1).split("/", 1)
82         bucket_name = bucket_args[0]
83         bucket_path = bucket_args[1] if len(bucket_args) > 1 else ""
84         bucket = storage_client.bucket(bucket_name)
85         prefix = bucket_path
86         if not prefix.endswith("/"):
87             prefix = prefix + "/"
88         blobs = bucket.list_blobs(prefix=prefix)
89         for blob in blobs:
90             # Replace any prefix from the object key with temp_dir
91             subdir_object_key = blob.name.replace(bucket_path, "", 1).strip("/")
92 
93             # Create necessary subdirectory to store the object locally
94             if "/" in subdir_object_key:
95                 local_object_dir = os.path.join(temp_dir, subdir_object_key.rsplit("/", 1)[0])
96                 if not os.path.isdir(local_object_dir):
97                     os.makedirs(local_object_dir, exist_ok=True)
98             if subdir_object_key.strip() != "":
99                 dest_path = os.path.join(temp_dir, subdir_object_key)
100                 logging.info("Downloading: %s", dest_path)
101                 blob.download_to_filename(dest_path)
102 
103     @staticmethod
104     def _download_blob(uri, out_dir: str):
105         match = re.search(_BLOB_RE, uri)
106         account_name = match.group(1)
107         storage_url = match.group(2)
108         container_name, prefix = storage_url.split("/", 1)
109 
110         logging.info("Connecting to BLOB account: %s, contianer: %s", account_name, container_name)
111         block_blob_service = BlockBlobService(account_name=account_name)
112         blobs = block_blob_service.list_blobs(container_name, prefix=prefix)
113 
114         for blob in blobs:
115             if "/" in blob.name:
116                 head, _ = os.path.split(blob.name)
117                 dir_path = os.path.join(out_dir, head)
118                 if not os.path.isdir(dir_path):
119                     os.makedirs(dir_path)
120 
121             dest_path = os.path.join(out_dir, blob.name)
122             logging.info("Downloading: %s", dest_path)
123             block_blob_service.get_blob_to_path(container_name, blob.name, dest_path)
124 
125     @staticmethod
126     def _download_local(uri, out_dir=None):
127         local_path = uri.replace(_LOCAL_PREFIX, "", 1)
128         if not os.path.exists(local_path):
129             raise Exception("Local path %s does not exist." % (uri))
130 
131         if out_dir is None:
132             return local_path
133         elif not os.path.isdir(out_dir):
134             os.makedirs(out_dir)
135 
136         if os.path.isdir(local_path):
137             local_path = os.path.join(local_path, "*")
138 
139         for src in glob.glob(local_path):
140             _, tail = os.path.split(src)
141             dest_path = os.path.join(out_dir, tail)
142             logging.info("Linking: %s to %s", src, dest_path)
143             os.symlink(src, dest_path)
144         return out_dir
145 
146     @staticmethod
147     def _create_minio_client():
148         # Remove possible http scheme for Minio
149         url = re.compile(r"https?://")
150         minioClient = Minio(url.sub("", os.getenv("S3_ENDPOINT", "")),
151                             access_key=os.getenv("AWS_ACCESS_KEY_ID", ""),
152                             secret_key=os.getenv("AWS_SECRET_ACCESS_KEY", ""),
153                             secure=True)
154         return minioClient
155
```
--- 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()

```

diff --git a/python/kfserving/kfserving/storage.py b/python/kfserving/kfserving/storage.py
--- a/python/kfserving/kfserving/storage.py
+++ b/python/kfserving/kfserving/storage.py
@@ -101,14 +101,15 @@
                 blob.download_to_filename(dest_path)
 
     @staticmethod
-    def _download_blob(uri, out_dir: str):
+    def _download_blob(uri, out_dir: str): # pylint: disable=too-many-locals
         match = re.search(_BLOB_RE, uri)
         account_name = match.group(1)
         storage_url = match.group(2)
         container_name, prefix = storage_url.split("/", 1)
 
         logging.info("Connecting to BLOB account: %s, contianer: %s", account_name, container_name)
-        block_blob_service = BlockBlobService(account_name=account_name)
+        token = Storage._get_azure_storage_token()
+        block_blob_service = BlockBlobService(account_name=account_name, token_credential=token)
         blobs = block_blob_service.list_blobs(container_name, prefix=prefix)
 
         for blob in blobs:
@@ -122,6 +123,33 @@
             logging.info("Downloading: %s", dest_path)
             block_blob_service.get_blob_to_path(container_name, blob.name, dest_path)
 
+    @staticmethod
+    def _get_azure_storage_token():
+        tenant_id = os.getenv("AZ_TENANT_ID", "")
+        client_id = os.getenv("AZ_CLIENT_ID", "")
+        client_secret = os.getenv("AZ_CLIENT_SECRET", "")
+        subscription_id = os.getenv("AZ_SUBSCRIPTION_ID", "")
+
+        if tenant_id == "" or client_id == "" or client_secret == "" or subscription_id == "":
+            return None
+
+        # note the SP must have "Storage Blob Data Owner" perms for this to work
+        import adal
+        from azure.storage.common import TokenCredential
+
+        authority_url = "https://login.microsoftonline.com/" + tenant_id
+
+        context = adal.AuthenticationContext(authority_url)
+
+        token = context.acquire_token_with_client_credentials(
+            "https://storage.azure.com/",
+            client_id,
+            client_secret)
+
+        token_credential = TokenCredential(token["accessToken"])
+
+        return token_credential
+
     @staticmethod
     def _download_local(uri, out_dir=None):
         local_path = uri.replace(_LOCAL_PREFIX, "", 1)

{"golden_diff": "diff --git a/python/kfserving/kfserving/storage.py b/python/kfserving/kfserving/storage.py\n--- a/python/kfserving/kfserving/storage.py\n+++ b/python/kfserving/kfserving/storage.py\n@@ -101,14 +101,15 @@\n                 blob.download_to_filename(dest_path)\n \n     @staticmethod\n-    def _download_blob(uri, out_dir: str):\n+    def _download_blob(uri, out_dir: str): # pylint: disable=too-many-locals\n         match = re.search(_BLOB_RE, uri)\n         account_name = match.group(1)\n         storage_url = match.group(2)\n         container_name, prefix = storage_url.split(\"/\", 1)\n \n         logging.info(\"Connecting to BLOB account: %s, contianer: %s\", account_name, container_name)\n-        block_blob_service = BlockBlobService(account_name=account_name)\n+        token = Storage._get_azure_storage_token()\n+        block_blob_service = BlockBlobService(account_name=account_name, token_credential=token)\n         blobs = block_blob_service.list_blobs(container_name, prefix=prefix)\n \n         for blob in blobs:\n@@ -122,6 +123,33 @@\n             logging.info(\"Downloading: %s\", dest_path)\n             block_blob_service.get_blob_to_path(container_name, blob.name, dest_path)\n \n+    @staticmethod\n+    def _get_azure_storage_token():\n+        tenant_id = os.getenv(\"AZ_TENANT_ID\", \"\")\n+        client_id = os.getenv(\"AZ_CLIENT_ID\", \"\")\n+        client_secret = os.getenv(\"AZ_CLIENT_SECRET\", \"\")\n+        subscription_id = os.getenv(\"AZ_SUBSCRIPTION_ID\", \"\")\n+\n+        if tenant_id == \"\" or client_id == \"\" or client_secret == \"\" or subscription_id == \"\":\n+            return None\n+\n+        # note the SP must have \"Storage Blob Data Owner\" perms for this to work\n+        import adal\n+        from azure.storage.common import TokenCredential\n+\n+        authority_url = \"https://login.microsoftonline.com/\" + tenant_id\n+\n+        context = adal.AuthenticationContext(authority_url)\n+\n+        token = context.acquire_token_with_client_credentials(\n+            \"https://storage.azure.com/\",\n+            client_id,\n+            client_secret)\n+\n+        token_credential = TokenCredential(token[\"accessToken\"])\n+\n+        return token_credential\n+\n     @staticmethod\n     def _download_local(uri, out_dir=None):\n         local_path = uri.replace(_LOCAL_PREFIX, \"\", 1)\n", "issue": "Support credentials for Azure Blobs\n/kind feature\r\n\r\n**Describe the solution you'd like**\r\n1. Credentials builder should support exposing azure blob credentials to model initializer\r\n2. Downloader should read and use credentials\r\n\r\n\r\n**Anything else you would like to add:**\r\nHere is how az creds are setup for kubeflow:  https://github.com/kubeflow/kubeflow/pull/2676\n", "before_files": [{"content": "# Copyright 2019 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 glob\nimport logging\nimport tempfile\nimport os\nimport re\nfrom azure.storage.blob import BlockBlobService\nfrom google.auth import exceptions\nfrom google.cloud import storage\nfrom minio import Minio\n\n_GCS_PREFIX = \"gs://\"\n_S3_PREFIX = \"s3://\"\n_BLOB_RE = \"https://(.+?).blob.core.windows.net/(.+)\"\n_LOCAL_PREFIX = \"file://\"\n\n\nclass Storage(object): # pylint: disable=too-few-public-methods\n    @staticmethod\n    def download(uri: str, out_dir: str = None) -> str:\n        logging.info(\"Copying contents of %s to local\", uri)\n\n        is_local = False\n        if uri.startswith(_LOCAL_PREFIX) or os.path.exists(uri):\n            is_local = True\n\n        if out_dir is None:\n            if is_local:\n                # noop if out_dir is not set and the path is local\n                return Storage._download_local(uri)\n            out_dir = tempfile.mkdtemp()\n\n        if uri.startswith(_GCS_PREFIX):\n            Storage._download_gcs(uri, out_dir)\n        elif uri.startswith(_S3_PREFIX):\n            Storage._download_s3(uri, out_dir)\n        elif re.search(_BLOB_RE, uri):\n            Storage._download_blob(uri, out_dir)\n        elif is_local:\n            return Storage._download_local(uri, out_dir)\n        else:\n            raise Exception(\"Cannot recognize storage type for \" + uri +\n                            \"\\n'%s', '%s', and '%s' are the current available storage type.\" %\n                            (_GCS_PREFIX, _S3_PREFIX, _LOCAL_PREFIX))\n\n        logging.info(\"Successfully copied %s to %s\", uri, out_dir)\n        return out_dir\n\n    @staticmethod\n    def _download_s3(uri, temp_dir: str):\n        client = Storage._create_minio_client()\n        bucket_args = uri.replace(_S3_PREFIX, \"\", 1).split(\"/\", 1)\n        bucket_name = bucket_args[0]\n        bucket_path = bucket_args[1] if len(bucket_args) > 1 else \"\"\n        objects = client.list_objects(bucket_name, prefix=bucket_path, recursive=True)\n        for obj in objects:\n            # Replace any prefix from the object key with temp_dir\n            subdir_object_key = obj.object_name.replace(bucket_path, \"\", 1).strip(\"/\")\n            client.fget_object(bucket_name, obj.object_name,\n                               os.path.join(temp_dir, subdir_object_key))\n\n    @staticmethod\n    def _download_gcs(uri, temp_dir: str):\n        try:\n            storage_client = storage.Client()\n        except exceptions.DefaultCredentialsError:\n            storage_client = storage.Client.create_anonymous_client()\n        bucket_args = uri.replace(_GCS_PREFIX, \"\", 1).split(\"/\", 1)\n        bucket_name = bucket_args[0]\n        bucket_path = bucket_args[1] if len(bucket_args) > 1 else \"\"\n        bucket = storage_client.bucket(bucket_name)\n        prefix = bucket_path\n        if not prefix.endswith(\"/\"):\n            prefix = prefix + \"/\"\n        blobs = bucket.list_blobs(prefix=prefix)\n        for blob in blobs:\n            # Replace any prefix from the object key with temp_dir\n            subdir_object_key = blob.name.replace(bucket_path, \"\", 1).strip(\"/\")\n\n            # Create necessary subdirectory to store the object locally\n            if \"/\" in subdir_object_key:\n                local_object_dir = os.path.join(temp_dir, subdir_object_key.rsplit(\"/\", 1)[0])\n                if not os.path.isdir(local_object_dir):\n                    os.makedirs(local_object_dir, exist_ok=True)\n            if subdir_object_key.strip() != \"\":\n                dest_path = os.path.join(temp_dir, subdir_object_key)\n                logging.info(\"Downloading: %s\", dest_path)\n                blob.download_to_filename(dest_path)\n\n    @staticmethod\n    def _download_blob(uri, out_dir: str):\n        match = re.search(_BLOB_RE, uri)\n        account_name = match.group(1)\n        storage_url = match.group(2)\n        container_name, prefix = storage_url.split(\"/\", 1)\n\n        logging.info(\"Connecting to BLOB account: %s, contianer: %s\", account_name, container_name)\n        block_blob_service = BlockBlobService(account_name=account_name)\n        blobs = block_blob_service.list_blobs(container_name, prefix=prefix)\n\n        for blob in blobs:\n            if \"/\" in blob.name:\n                head, _ = os.path.split(blob.name)\n                dir_path = os.path.join(out_dir, head)\n                if not os.path.isdir(dir_path):\n                    os.makedirs(dir_path)\n\n            dest_path = os.path.join(out_dir, blob.name)\n            logging.info(\"Downloading: %s\", dest_path)\n            block_blob_service.get_blob_to_path(container_name, blob.name, dest_path)\n\n    @staticmethod\n    def _download_local(uri, out_dir=None):\n        local_path = uri.replace(_LOCAL_PREFIX, \"\", 1)\n        if not os.path.exists(local_path):\n            raise Exception(\"Local path %s does not exist.\" % (uri))\n\n        if out_dir is None:\n            return local_path\n        elif not os.path.isdir(out_dir):\n            os.makedirs(out_dir)\n\n        if os.path.isdir(local_path):\n            local_path = os.path.join(local_path, \"*\")\n\n        for src in glob.glob(local_path):\n            _, tail = os.path.split(src)\n            dest_path = os.path.join(out_dir, tail)\n            logging.info(\"Linking: %s to %s\", src, dest_path)\n            os.symlink(src, dest_path)\n        return out_dir\n\n    @staticmethod\n    def _create_minio_client():\n        # Remove possible http scheme for Minio\n        url = re.compile(r\"https?://\")\n        minioClient = Minio(url.sub(\"\", os.getenv(\"S3_ENDPOINT\", \"\")),\n                            access_key=os.getenv(\"AWS_ACCESS_KEY_ID\", \"\"),\n                            secret_key=os.getenv(\"AWS_SECRET_ACCESS_KEY\", \"\"),\n                            secure=True)\n        return minioClient\n", "path": "python/kfserving/kfserving/storage.py"}], "after_files": [{"content": "# Copyright 2019 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 glob\nimport logging\nimport tempfile\nimport os\nimport re\nfrom azure.storage.blob import BlockBlobService\nfrom google.auth import exceptions\nfrom google.cloud import storage\nfrom minio import Minio\n\n_GCS_PREFIX = \"gs://\"\n_S3_PREFIX = \"s3://\"\n_BLOB_RE = \"https://(.+?).blob.core.windows.net/(.+)\"\n_LOCAL_PREFIX = \"file://\"\n\n\nclass Storage(object): # pylint: disable=too-few-public-methods\n    @staticmethod\n    def download(uri: str, out_dir: str = None) -> str:\n        logging.info(\"Copying contents of %s to local\", uri)\n\n        is_local = False\n        if uri.startswith(_LOCAL_PREFIX) or os.path.exists(uri):\n            is_local = True\n\n        if out_dir is None:\n            if is_local:\n                # noop if out_dir is not set and the path is local\n                return Storage._download_local(uri)\n            out_dir = tempfile.mkdtemp()\n\n        if uri.startswith(_GCS_PREFIX):\n            Storage._download_gcs(uri, out_dir)\n        elif uri.startswith(_S3_PREFIX):\n            Storage._download_s3(uri, out_dir)\n        elif re.search(_BLOB_RE, uri):\n            Storage._download_blob(uri, out_dir)\n        elif is_local:\n            return Storage._download_local(uri, out_dir)\n        else:\n            raise Exception(\"Cannot recognize storage type for \" + uri +\n                            \"\\n'%s', '%s', and '%s' are the current available storage type.\" %\n                            (_GCS_PREFIX, _S3_PREFIX, _LOCAL_PREFIX))\n\n        logging.info(\"Successfully copied %s to %s\", uri, out_dir)\n        return out_dir\n\n    @staticmethod\n    def _download_s3(uri, temp_dir: str):\n        client = Storage._create_minio_client()\n        bucket_args = uri.replace(_S3_PREFIX, \"\", 1).split(\"/\", 1)\n        bucket_name = bucket_args[0]\n        bucket_path = bucket_args[1] if len(bucket_args) > 1 else \"\"\n        objects = client.list_objects(bucket_name, prefix=bucket_path, recursive=True)\n        for obj in objects:\n            # Replace any prefix from the object key with temp_dir\n            subdir_object_key = obj.object_name.replace(bucket_path, \"\", 1).strip(\"/\")\n            client.fget_object(bucket_name, obj.object_name,\n                               os.path.join(temp_dir, subdir_object_key))\n\n    @staticmethod\n    def _download_gcs(uri, temp_dir: str):\n        try:\n            storage_client = storage.Client()\n        except exceptions.DefaultCredentialsError:\n            storage_client = storage.Client.create_anonymous_client()\n        bucket_args = uri.replace(_GCS_PREFIX, \"\", 1).split(\"/\", 1)\n        bucket_name = bucket_args[0]\n        bucket_path = bucket_args[1] if len(bucket_args) > 1 else \"\"\n        bucket = storage_client.bucket(bucket_name)\n        prefix = bucket_path\n        if not prefix.endswith(\"/\"):\n            prefix = prefix + \"/\"\n        blobs = bucket.list_blobs(prefix=prefix)\n        for blob in blobs:\n            # Replace any prefix from the object key with temp_dir\n            subdir_object_key = blob.name.replace(bucket_path, \"\", 1).strip(\"/\")\n\n            # Create necessary subdirectory to store the object locally\n            if \"/\" in subdir_object_key:\n                local_object_dir = os.path.join(temp_dir, subdir_object_key.rsplit(\"/\", 1)[0])\n                if not os.path.isdir(local_object_dir):\n                    os.makedirs(local_object_dir, exist_ok=True)\n            if subdir_object_key.strip() != \"\":\n                dest_path = os.path.join(temp_dir, subdir_object_key)\n                logging.info(\"Downloading: %s\", dest_path)\n                blob.download_to_filename(dest_path)\n\n    @staticmethod\n    def _download_blob(uri, out_dir: str): # pylint: disable=too-many-locals\n        match = re.search(_BLOB_RE, uri)\n        account_name = match.group(1)\n        storage_url = match.group(2)\n        container_name, prefix = storage_url.split(\"/\", 1)\n\n        logging.info(\"Connecting to BLOB account: %s, contianer: %s\", account_name, container_name)\n        token = Storage._get_azure_storage_token()\n        block_blob_service = BlockBlobService(account_name=account_name, token_credential=token)\n        blobs = block_blob_service.list_blobs(container_name, prefix=prefix)\n\n        for blob in blobs:\n            if \"/\" in blob.name:\n                head, _ = os.path.split(blob.name)\n                dir_path = os.path.join(out_dir, head)\n                if not os.path.isdir(dir_path):\n                    os.makedirs(dir_path)\n\n            dest_path = os.path.join(out_dir, blob.name)\n            logging.info(\"Downloading: %s\", dest_path)\n            block_blob_service.get_blob_to_path(container_name, blob.name, dest_path)\n\n    @staticmethod\n    def _get_azure_storage_token():\n        tenant_id = os.getenv(\"AZ_TENANT_ID\", \"\")\n        client_id = os.getenv(\"AZ_CLIENT_ID\", \"\")\n        client_secret = os.getenv(\"AZ_CLIENT_SECRET\", \"\")\n        subscription_id = os.getenv(\"AZ_SUBSCRIPTION_ID\", \"\")\n\n        if tenant_id == \"\" or client_id == \"\" or client_secret == \"\" or subscription_id == \"\":\n            return None\n\n        # note the SP must have \"Storage Blob Data Owner\" perms for this to work\n        import adal\n        from azure.storage.common import TokenCredential\n\n        authority_url = \"https://login.microsoftonline.com/\" + tenant_id\n\n        context = adal.AuthenticationContext(authority_url)\n\n        token = context.acquire_token_with_client_credentials(\n            \"https://storage.azure.com/\",\n            client_id,\n            client_secret)\n\n        token_credential = TokenCredential(token[\"accessToken\"])\n\n        return token_credential\n\n    @staticmethod\n    def _download_local(uri, out_dir=None):\n        local_path = uri.replace(_LOCAL_PREFIX, \"\", 1)\n        if not os.path.exists(local_path):\n            raise Exception(\"Local path %s does not exist.\" % (uri))\n\n        if out_dir is None:\n            return local_path\n        elif not os.path.isdir(out_dir):\n            os.makedirs(out_dir)\n\n        if os.path.isdir(local_path):\n            local_path = os.path.join(local_path, \"*\")\n\n        for src in glob.glob(local_path):\n            _, tail = os.path.split(src)\n            dest_path = os.path.join(out_dir, tail)\n            logging.info(\"Linking: %s to %s\", src, dest_path)\n            os.symlink(src, dest_path)\n        return out_dir\n\n    @staticmethod\n    def _create_minio_client():\n        # Remove possible http scheme for Minio\n        url = re.compile(r\"https?://\")\n        minioClient = Minio(url.sub(\"\", os.getenv(\"S3_ENDPOINT\", \"\")),\n                            access_key=os.getenv(\"AWS_ACCESS_KEY_ID\", \"\"),\n                            secret_key=os.getenv(\"AWS_SECRET_ACCESS_KEY\", \"\"),\n                            secure=True)\n        return minioClient\n", "path": "python/kfserving/kfserving/storage.py"}]}