Repository: kserve/kserve

Issue:
SDK Support create credentials for Azure
/kind feature

**Describe the solution you'd like**
As KFService support credentials for Azure in #292, SDK also should support create credentials for Azure.


**Anything else you would like to add:**
[Miscellaneous information that will assist in solving the issue.]



Discussion:
/assign @jinchihe 
/cc @rakelkar 
I'm going to do that.
Design:

1. Get the `AZ_CLIENT_ID`, `AZ_CLIENT_SECRET`, `AZ_SUBSCRIPTION_ID` and `AZ_TENANT_ID` from the azure creds file (Defaults can be ~/.azure/azure_credentials.json from [here](https://docs.microsoft.com/en-us/azure/python/python-sdk-azure-authenticate)). 
2. Create the Secret, and named `azcreds` (try to get the secret name from kfservice configmap), if the azcreds existing, update the key/value in data section.
3. Attach the `Secret` to specified/created Service Account.

@rakelkar Seem the secret name must be `azcreds`? could you please confirm if SDK can created the secret name that's not `azcreds`?

Resolving patch:
diff --git a/python/kfserving/kfserving/api/creds_utils.py b/python/kfserving/kfserving/api/creds_utils.py
--- a/python/kfserving/kfserving/api/creds_utils.py
+++ b/python/kfserving/kfserving/api/creds_utils.py
@@ -112,6 +112,34 @@ def set_s3_credentials(namespace, credentials_file, service_account, s3_profile=
                         service_account=service_account,
                         secret_name=secret_name)
 
+def set_azure_credentials(namespace, credentials_file, service_account):
+    '''
+    Set Azure Credentails (secret and service account) with credentials file.
+    Args:
+        namespace(str): The kubernetes namespace.
+        credentials_file(str): The path for the Azure credentials file.
+        service_account(str): The name of service account. If the service_account
+                              is specified, will attach created secret with the service account,
+                              otherwise will create new one and attach with created secret.
+    '''
+
+    with open(expanduser(credentials_file)) as azure_creds_file:
+        azure_creds = json.load(azure_creds_file)
+
+    data = {
+        'AZ_CLIENT_ID': azure_creds['clientId'],
+        'AZ_CLIENT_SECRET': azure_creds['clientSecret'],
+        'AZ_SUBSCRIPTION_ID': azure_creds['subscriptionId'],
+        'AZ_TENANT_ID': azure_creds['tenantId'],
+        }
+
+    secret_name = create_secret(
+        namespace=namespace, data=data)
+
+    set_service_account(namespace=namespace,
+                        service_account=service_account,
+                        secret_name=secret_name)
+
 
 def create_secret(namespace, annotations=None, data=None, string_data=None):
     'Create namespaced secret, and return the secret name.'
diff --git a/python/kfserving/kfserving/api/kf_serving_client.py b/python/kfserving/kfserving/api/kf_serving_client.py
--- a/python/kfserving/kfserving/api/kf_serving_client.py
+++ b/python/kfserving/kfserving/api/kf_serving_client.py
@@ -16,7 +16,7 @@
 
 from ..constants import constants
 from ..utils import utils
-from .creds_utils import set_gcs_credentials, set_s3_credentials
+from .creds_utils import set_gcs_credentials, set_s3_credentials, set_azure_credentials
 
 
 class KFServingClient(object):
@@ -62,8 +62,14 @@ def set_credentials(self, storage_type, namespace=None, credentials_file=None,
                                credentials_file=credentials_file,
                                service_account=service_account,
                                **kwargs)
+        elif storage_type.lower() == 'azure':
+            if credentials_file is None:
+                credentials_file = constants.AZ_DEFAULT_CREDS_FILE
+            set_azure_credentials(namespace=namespace,
+                                  credentials_file=credentials_file,
+                                  service_account=service_account)
         else:
-            raise RuntimeError("Invalid storage_type: %s, only support GCS and S3\
+            raise RuntimeError("Invalid storage_type: %s, only support GCS, S3 and Azure\
                 currently.\n" % storage_type)
 
 
diff --git a/python/kfserving/kfserving/constants/constants.py b/python/kfserving/kfserving/constants/constants.py
--- a/python/kfserving/kfserving/constants/constants.py
+++ b/python/kfserving/kfserving/constants/constants.py
@@ -18,3 +18,6 @@
 # GCS credentials constants
 GCS_CREDS_FILE_DEFAULT_NAME = 'gcloud-application-credentials.json'
 GCS_DEFAULT_CREDS_FILE = '~/.config/gcloud/application_default_credentials.json'
+
+# Azure credentials constants
+AZ_DEFAULT_CREDS_FILE = '~/.azure/azure_credentials.json'


Test patch:
diff --git a/python/kfserving/test/azure_credentials.json b/python/kfserving/test/azure_credentials.json
new file mode 100644
--- /dev/null
+++ b/python/kfserving/test/azure_credentials.json
@@ -0,0 +1,6 @@
+{
+    "clientId": "YTJhYjExYWYtMDFhYS00NzU5LTgzNDUtNzgwMzI4N2RiZD",
+    "clientSecret": "password",
+    "subscriptionId": "MzMzMzMzMzMtMzMzMy0zMzMzLTMzMzMtMzMzMzMz",
+    "tenantId": "QUJDREVGR0gtMTIzNC0xMjM0LTEyMzQtQUJDREVGR0hJSk"
+}
diff --git a/python/kfserving/test/test_set_creds.py b/python/kfserving/test/test_set_creds.py
--- a/python/kfserving/test/test_set_creds.py
+++ b/python/kfserving/test/test_set_creds.py
@@ -93,3 +93,20 @@ def test_set_credentials_gcp():
     created_secret_name = created_sa.secrets[0].name
     created_secret = get_created_secret(created_secret_name)
     assert created_secret.data[constants.GCS_CREDS_FILE_DEFAULT_NAME] == gcp_testing_creds
+
+
+def test_azure_credentials():
+    '''Test Azure credentials creating'''
+    KFServing = KFServingClient()
+    sa_name = constants.DEFAULT_SA_NAME
+    KFServing.set_credentials(storage_type='Azure',
+                              namespace='kubeflow',
+                              credentials_file='./azure_credentials.json',
+                              sa_name=sa_name)
+    created_sa = get_created_sa(sa_name)
+    created_secret_name = created_sa.secrets[0].name
+    created_secret = get_created_secret(created_secret_name)
+    assert created_secret.data['AZ_CLIENT_ID'] == 'YTJhYjExYWYtMDFhYS00NzU5LTgzNDUtNzgwMzI4N2RiZD'
+    assert created_secret.data['AZ_CLIENT_SECRET'] == 'password'
+    assert created_secret.data['AZ_SUBSCRIPTION_ID'] == 'MzMzMzMzMzMtMzMzMy0zMzMzLTMzMzMtMzMzMzMz'
+    assert created_secret.data['AZ_TENANT_ID'] == 'QUJDREVGR0gtMTIzNC0xMjM0LTEyMzQtQUJDREVGR0hJSk'