added input validation, updated sdk and fixed bug

This commit is contained in:
Marvin Buss 2020-05-03 13:24:52 +02:00
Родитель fa4bb4de0f
Коммит bbb4ae0398
13 изменённых файлов: 519 добавлений и 53 удалений

Просмотреть файл

@ -17,18 +17,13 @@
"cpu_cores": 0.1,
"memory_gb": 0.5,
"delete_service_after_test": false,
"no_code_deployment_enabled": false,
"tags": {"<your-webservice-tag-key>": "<your-webservice-tag-value>"},
"properties": {"<your-webservice-property-key>": "<your-webservice-property-value>"},
"description": "<your-webservice-description>",
"location": "<your-aci-location>",
"gpu_cores": 0,
"ssl_enabled": true,
"ssl_cert_pem_file": "<your-aci-ssl-cert-pem-file>",
"ssl_key_pem_file": "<your-aci-ssl-key-pem-file>",
"ssl_cname": "<your-aci-ssl-cname>",
"dns_name_label": "<your-aci-dns-name-label>",
"cmk_vault_base_url": "<your-aci-cmk-vault-base-url>",
"cmk_key_name": "<your-aci-cmk-key-name>",
"cmk_key_version": "<your-aci-cmk-key-version>"
"dns_name_label": "<your-aci-dns-name-label>"
}

Просмотреть файл

@ -17,8 +17,8 @@
"custom_base_image": "<your-custom-docker-base-image>",
"cpu_cores": 0.1,
"memory_gb": 0.5,
"gpu_cores": 0,
"delete_service_after_test": false,
"no_code_deployment_enabled": false,
"tags": {"<your-webservice-tag-key>": "<your-webservice-tag-value>"},
"properties": {"<your-webservice-property-key>": "<your-webservice-property-value>"},
"description": "<your-webservice-description>",

Просмотреть файл

@ -3,11 +3,11 @@
"deployment_compute_target": "<your-deployment-compute-target-name>", // do not specify deployment compute target name for deployment on Azure Container Registry
"inference_source_directory": "<your-inference-source-directory>",
"inference_entry_script": "<your-inference-entry-script>",
"conda_file": "<your-conda-environment-file-path>",
"extra_docker_file_steps": "<your-extra-docker-steps-file-path>",
"test_enabled": true,
"test_file_path": "<your-test-file-path>",
"test_file_function_name": "<your-test-file-function-name>",
"conda_file": "<your-conda-environment-file-path>",
"extra_docker_file_steps": "<your-extra-docker-steps-file-path>",
"enable_gpu": false,
"cuda_version": "<your-cuda-version>",
"model_data_collection_enabled": true,
@ -18,24 +18,20 @@
"cpu_cores": 0.1,
"memory_gb": 0.5,
"delete_service_after_test": false,
"no_code_deployment_enabled": false,
"tags": {"<your-webservice-tag-key>": "<your-webservice-tag-value>"},
"properties": {"<your-webservice-property-key>": "<your-webservice-property-value>"},
"description": "<your-webservice-description>",
// aci specific parameters
"location": "<your-aci-location>",
"gpu_cores": 0,
"ssl_enabled": true,
"ssl_cert_pem_file": "<your-aci-ssl-cert-pem-file>",
"ssl_key_pem_file": "<your-aci-ssl-key-pem-file>",
"ssl_cname": "<your-aci-ssl-cname>",
"dns_name_label": "<your-aci-dns-name-label>",
"cmk_vault_base_url": "<your-aci-cmk-vault-base-url>",
"cmk_key_name": "<your-aci-cmk-key-name>",
"cmk_key_version": "<your-aci-cmk-key-version>",
// aks specific parameters
"gpu_cores": 0,
"autoscale_enabled": true,
"autoscale_min_replicas": 1,
"autoscale_max_replicas": 10,

14
.github/workflows/python.yml поставляемый
Просмотреть файл

@ -1,8 +1,8 @@
name: Lint
name: Lint and Test
on: [push, pull_request]
jobs:
lint:
name: Lint
name: Lint and Test
runs-on: ubuntu-latest
steps:
- name: Set up Python 3.7
@ -19,5 +19,11 @@ jobs:
id: python_linting
run: |
pip install flake8
flake8 --ignore E501 code/main.py
flake8 --ignore E501 code/utils.py
flake8 code/ --count --ignore=E501 --show-source --statistics
flake8 tests/ --count --ignore=E501,E402 --show-source --statistics
- name: Test
id: python_test
run: |
pip install pytest jsonschema azureml-sdk
pytest

134
.gitignore поставляемый
Просмотреть файл

@ -1,5 +1,131 @@
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
# Byte-compiled / optimized / DLL files
.vs/
__pycache__/
*.py[cod]
*$py.class
/.vs
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
.vs/

Просмотреть файл

@ -1,5 +1,5 @@
FROM marvinbuss/aml-docker:1.1.5
FROM marvinbuss/aml-docker:1.4.0
LABEL maintainer="azure/gh_aml"

Просмотреть файл

@ -111,13 +111,13 @@ A sample file can be found in this repository in the folder `.cloud/.azure`. The
| inference_entry_script | | str | `"score.py"` | The path to a local file in your repository that contains the code to run for the image and score the data. This path is relative to the specified source directory. The python script has to define an `init` and a `run` function. A sample can be found in the template repositories. |
| conda_file | | str | `"environment.yml"` | The path to a local file in your repository containing a conda environment definition to use for the image. This path is relative to the specified source directory. |
| extra_docker_file_steps | | str | null | The path to a local file in your repository containing additional Docker steps to run when setting up image. This path is relative to the specified source directory. |
| enable_gpu | | str | false | Indicates whether to enable GPU support in the image. The GPU image must be used on Microsoft Azure Services such as Azure Container Instances, Azure Machine Learning Compute, Azure Virtual Machines, and Azure Kubernetes Service. |
| enable_gpu | | bool | false | Indicates whether to enable GPU support in the image. The GPU image must be used on Microsoft Azure Services such as Azure Container Instances, Azure Machine Learning Compute, Azure Virtual Machines, and Azure Kubernetes Service. |
| cuda_version | | str | `"9.1"` if `enable_gpu` is set to true | The Version of CUDA to install for images that need GPU support. The GPU image must be used on Microsoft Azure Services such as Azure Container Instances, Azure Machine Learning Compute, Azure Virtual Machines, and Azure Kubernetes Service. Supported versions are 9.0, 9.1, and 10.0. |
| runtime | | str: `"python"` or `"spark-py"` | `"python"` | The runtime to use for the image. |
| custom_base_image | | str | null | A custom Docker image to be used as base image. If no base image is given then the base image will be used based off of given runtime parameter. |
| model_data_collection_enabled | | bool | false | Whether or not to enable model data collection for this Webservice. |
| authentication_enabled | | bool | false for ACI, true for AKS | Whether or not to enable key auth for this Webservice. |
| app_insights_enabled | | bool | false | Whether or not to enable Application Insights logging for this Webservice. |
| app_insights_enabled | | bool | false | Whether or not to enable Application Insights logging for this Webservice. |
| cpu_cores | | float | 0.1 | The number of CPU cores to allocate for this Webservice. Can be a decimal. |
| memory_gb | | float | 0.5 | The amount of memory (in GB) to allocate for this Webservice. Can be a decimal. |
| delete_service_after_deployment | | bool | false | Indicates whether the service gets deleted after the deployment completed successfully. |
@ -139,7 +139,7 @@ ACI is the default deployment resource. A sample file for an aci deployment can
| location | | str: [supported region](https://azure.microsoft.com/en-us/global-infrastructure/services/?products=container-instances) | workspace location | The Azure region to deploy this Webservice to. |
| ssl_enabled | | bool | false | Whether or not to enable SSL for this Webservice. |
| ssl_cert_pem_file | | str | null | A file path to a file containing cert information for SSL validation. Must provide all three CName, cert file, and key file to enable SSL validation. |
| ssl_key_pem_file | | st | null | A file path to a file containing key information for SSL validation. Must provide all three CName, cert file, and key file to enable SSL validation. |
| ssl_key_pem_file | | str | null | A file path to a file containing key information for SSL validation. Must provide all three CName, cert file, and key file to enable SSL validation. |
| ssl_cname | | str | null | A CName to use if enabling SSL validation on the cluster. Must provide all three CName, cert file, and key file to enable SSL validation. |
| dns_name_label | | str | null | The DNS name label for the scoring endpoint. If not specified a unique DNS name label will be generated for the scoring endpoint. |
@ -151,14 +151,14 @@ For the deployment of the model to AKS, you must configure an AKS resource and s
| Parameter | Required | Allowed Values | Default | Description |
| ----------------------- | -------- | -------------- | ---------- | ----------- |
| gpu_cores | | float | 1 | The number of GPU cores to allocate for this Webservice. |
| autoscale_enabled | | bool | true if `num_replicas` is null | Whether or not to enable autoscaling for this Webservice. |
| gpu_cores | | int | 1 | The number of GPU cores to allocate for this Webservice. |
| autoscale_enabled | | bool | true if `num_replicas` is null | Whether to enable autoscale for this Webservice. |
| autoscale_min_replicas | | int | 1 | The minimum number of containers to use when autoscaling this Webservice. |
| autoscale_max_replicas | | int | 10 | The maximum number of containers to use when autoscaling this Webservice. |
| autoscale_refresh_seconds | | int | 1 | How often the autoscaler should attempt to scale this Webservice. |
| autoscale_refresh_seconds | | int | 1 | How often the autoscaler should attempt to scale this Webservice (in seconds). |
| autoscale_target_utilization| | int | 70 | The target utilization (in percent out of 100) the autoscaler should attempt to maintain for this Webservice. |
| scoring_timeout_ms | | int | 60000 | A timeout in ms to enforce for scoring calls to this Webservice. |
| replica_max_concurrent_requests| | float | 1 | The number of maximum concurrent requests per replica to allow for this Webservice. **Do not change this setting from the default value of 1 unless instructed by Microsoft Technical Support or a member of Azure Machine Learning team.** |
| replica_max_concurrent_requests| | int | 1 | The number of maximum concurrent requests per replica to allow for this Webservice. **Do not change this setting from the default value of 1 unless instructed by Microsoft Technical Support or a member of Azure Machine Learning team.** |
| max_request_wait_time | | int | 500 | The maximum amount of time a request will stay in the queue (in milliseconds) before returning a 503 error. |
| num_replicas | | int | null | The number of containers to allocate for this Webservice. **No default, if this parameter is not set then the autoscaler is enabled by default.** |
| period_seconds | | int: [1, inf[ | 10 | How often (in seconds) to perform the liveness probe. |
@ -167,7 +167,7 @@ For the deployment of the model to AKS, you must configure an AKS resource and s
| success_threshold | | int: [1, inf[ | 1 | The minimum consecutive successes for the liveness probe to be considered successful after having failed. |
| failure_threshold | | int: [1, inf[ | 3 | When a Pod starts and the liveness probe fails, Kubernetes will try failureThreshold times before giving up. |
| namespace | | str | null | The Kubernetes namespace in which to deploy this Webservice: up to 63 lowercase alphanumeric ('a'-'z', '0'-'9') and hyphen ('-') characters. The first and last characters cannot be hyphens. |
| token_auth_enabled | | bool | false | Whether or not to enable Token auth for this Webservice. If this is enabled, users can access this Webservice by fetching an access token using their Azure Active Directory credentials. |
| token_auth_enabled | | bool | false | Whether to enable Token authentication for this Webservice. If this is enabled, users can access this Webservice by fetching an access token using their Azure Active Directory credentials. |
Please visit [this website](https://docs.microsoft.com/en-us/python/api/azureml-core/azureml.core.webservice.aks.akswebservice?view=azure-ml-py#deploy-configuration-autoscale-enabled-none--autoscale-min-replicas-none--autoscale-max-replicas-none--autoscale-refresh-seconds-none--autoscale-target-utilization-none--collect-model-data-none--auth-enabled-none--cpu-cores-none--memory-gb-none--enable-app-insights-none--scoring-timeout-ms-none--replica-max-concurrent-requests-none--max-request-wait-time-none--num-replicas-none--primary-key-none--secondary-key-none--tags-none--properties-none--description-none--gpu-cores-none--period-seconds-none--initial-delay-seconds-none--timeout-seconds-none--success-threshold-none--failure-threshold-none--namespace-none--token-auth-enabled-none--compute-target-name-none-) for more details. More Information on autoscaling parameters can be found [here](https://docs.microsoft.com/en-us/python/api/azureml-core/azureml.core.webservice.aks.autoscaler?view=azure-ml-py) and for liveness probe [here](https://docs.microsoft.com/en-us/python/api/azureml-core/azureml.core.webservice.aks.livenessproberequirements?view=azure-ml-py).

Просмотреть файл

@ -2,5 +2,5 @@
set -e
ls -la
ls -la code
python /code/main.py

Просмотреть файл

@ -12,24 +12,18 @@ from azureml.core.authentication import ServicePrincipalAuthentication
from adal.adal_error import AdalError
from msrest.exceptions import AuthenticationError
from json import JSONDecodeError
from utils import AMLConfigurationException, AMLDeploymentException, required_parameters_provided, get_resource_config, mask_parameter
from utils import AMLConfigurationException, AMLDeploymentException, get_resource_config, mask_parameter, validate_json
from schemas import azure_credentials_schema, parameters_schema
def main():
# Loading input values
print("::debug::Loading input values")
parameters_file = os.environ.get("INPUT_PARAMETERS_FILE", default="deploy.json")
azure_credentials = os.environ.get("INPUT_AZURE_CREDENTIALS", default="{}")
model_name = os.environ.get("INPUT_MODEL_NAME", default=None)
model_version = os.environ.get("INPUT_MODEL_VERSION", default=None)
# Casting input values
print("::debug::Casting input values")
try:
azure_credentials = json.loads(azure_credentials)
except JSONDecodeError:
print("::error::Please paste output of `az ad sp create-for-rbac --name <your-sp-name> --role contributor --scopes /subscriptions/<your-subscriptionId>/resourceGroups/<your-rg> --sdk-auth` as value of secret variable: AZURE_CREDENTIALS")
raise AMLConfigurationException(f"Incorrect or poorly formed output from azure credentials saved in AZURE_CREDENTIALS secret. See setup in https://github.com/Azure/aml-compute/blob/master/README.md")
try:
model_version = int(model_version)
except TypeError as exception:
@ -39,12 +33,21 @@ def main():
print(f"::debug::Could not cast model version to int: {exception}")
model_version = None
# Loading azure credentials
print("::debug::Loading azure credentials")
azure_credentials = os.environ.get("INPUT_AZURE_CREDENTIALS", default="{}")
try:
azure_credentials = json.loads(azure_credentials)
except JSONDecodeError:
print("::error::Please paste output of `az ad sp create-for-rbac --name <your-sp-name> --role contributor --scopes /subscriptions/<your-subscriptionId>/resourceGroups/<your-rg> --sdk-auth` as value of secret variable: AZURE_CREDENTIALS")
raise AMLConfigurationException(f"Incorrect or poorly formed output from azure credentials saved in AZURE_CREDENTIALS secret. See setup in https://github.com/Azure/aml-compute/blob/master/README.md")
# Checking provided parameters
print("::debug::Checking provided parameters")
required_parameters_provided(
parameters=azure_credentials,
keys=["tenantId", "clientId", "clientSecret"],
message="Required parameter(s) not found in your azure credentials saved in AZURE_CREDENTIALS secret for logging in to the workspace. Please provide a value for the following key(s): "
validate_json(
data=azure_credentials,
schema=azure_credentials_schema,
input_name="AZURE_CREDENTIALS"
)
# Mask values
@ -56,6 +59,7 @@ def main():
# Loading parameters file
print("::debug::Loading parameters file")
parameters_file = os.environ.get("INPUT_PARAMETERS_FILE", default="deploy.json")
parameters_file_path = os.path.join(".cloud", ".azure", parameters_file)
try:
with open(parameters_file_path) as f:
@ -63,6 +67,14 @@ def main():
except FileNotFoundError:
print(f"::debug::Could not find parameter file in {parameters_file_path}. Please provide a parameter file in your repository if you do not want to use default settings (e.g. .cloud/.azure/deploy.json).")
parameters = {}
# Checking provided parameters
print("::debug::Checking provided parameters")
validate_json(
data=parameters,
schema=parameters_schema,
input_name="PARAMETERS_FILE"
)
# Loading Workspace
print("::debug::Loading AML Workspace")

234
code/schemas.py Normal file
Просмотреть файл

@ -0,0 +1,234 @@
azure_credentials_schema = {
"$id": "http://azure-ml.com/schemas/azure_credentials.json",
"$schema": "http://json-schema.org/schema",
"title": "azure_credentials",
"description": "JSON specification for your azure credentials",
"type": "object",
"required": ["clientId", "clientSecret", "subscriptionId", "tenantId"],
"properties": {
"clientId": {
"type": "string",
"description": "The client ID of the service principal."
},
"clientSecret": {
"type": "string",
"description": "The client secret of the service principal."
},
"subscriptionId": {
"type": "string",
"description": "The subscription ID that should be used."
},
"tenantId": {
"type": "string",
"description": "The tenant ID of the service principal."
}
}
}
parameters_schema = {
"$id": "http://azure-ml.com/schemas/deploy.json",
"$schema": "http://json-schema.org/schema",
"title": "aml-registermodel",
"description": "JSON specification for your deploy details",
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "The name to give the deployed service.",
"minLength": 3,
"maxLength": 32
},
"deployment_compute_target": {
"type": "string",
"description": "Name of the compute target to deploy the webservice to."
},
"inference_source_directory": {
"type": "string",
"description": "The path to the folder that contains all files to create the image."
},
"inference_entry_script": {
"type": "string",
"description": "The path to a local file in your repository that contains the code to run for the image and score the data."
},
"test_enabled": {
"type": "boolean",
"description": "Whether to run tests for this model deployment and the created real-time endpoint."
},
"test_file_path": {
"type": "string",
"description": "Path to the python script in your repository in which you define your own tests that you want to run against the webservice endpoint."
},
"test_file_function_name": {
"type": "string",
"description": "Name of the function in your python script in your repository in which you define your own tests that you want to run against the webservice endpoint."
},
"conda_file": {
"type": "string",
"description": "The path to a local file in your repository containing a conda environment definition to use for the image."
},
"extra_docker_file_steps": {
"type": "string",
"description": "The path to a local file in your repository containing additional Docker steps to run when setting up image."
},
"enable_gpu": {
"type": "boolean",
"description": "Indicates whether to enable GPU support in the image."
},
"cuda_version": {
"type": "string",
"description": "The Version of CUDA to install for images that need GPU support."
},
"model_data_collection_enabled": {
"type": "boolean",
"description": "Whether or not to enable model data collection for this Webservice."
},
"authentication_enabled": {
"type": "boolean",
"description": "Whether or not to enable key auth for this Webservice."
},
"app_insights_enabled": {
"type": "boolean",
"description": "Whether or not to enable Application Insights logging for this Webservice."
},
"runtime": {
"type": "string",
"description": "The runtime to use for the image.",
"pattern": "python|spark-py"
},
"custom_base_image": {
"type": "string",
"description": "A custom Docker image to be used as base image."
},
"cpu_cores": {
"type": "number",
"description": "The number of CPU cores to allocate for this Webservice."
},
"memory_gb": {
"type": "number",
"description": "The amount of memory (in GB) to allocate for this Webservice."
},
"delete_service_after_test": {
"type": "boolean",
"description": "Indicates whether the service gets deleted after the deployment completed successfully."
},
"tags": {
"type": "object",
"description": "Dictionary of key value tags to give this Webservice."
},
"properties": {
"type": "object",
"description": "Dictionary of key value properties to give this Webservice."
},
"description": {
"type": "string",
"description": "A description to give this Webservice and image."
},
"location": {
"type": "string",
"description": "The Azure region to deploy this Webservice to."
},
"ssl_enabled": {
"type": "boolean",
"description": "Whether or not to enable SSL for this Webservice."
},
"ssl_cert_pem_file": {
"type": "string",
"description": "A file path to a file containing cert information for SSL validation."
},
"ssl_key_pem_file": {
"type": "string",
"description": "A file path to a file containing key information for SSL validation."
},
"ssl_cname": {
"type": "string",
"description": "A CName to use if enabling SSL validation on the cluster."
},
"dns_name_label": {
"type": "string",
"description": "The DNS name label for the scoring endpoint."
},
"gpu_cores": {
"type": "integer",
"description": "The number of GPU cores to allocate for this Webservice."
},
"autoscale_enabled": {
"type": "boolean",
"description": "Whether to enable autoscale for this Webservice."
},
"autoscale_min_replicas": {
"type": "integer",
"description": "The minimum number of containers to use when autoscaling this Webservice.",
"minimum": 1
},
"autoscale_max_replicas": {
"type": "integer",
"description": "The maximum number of containers to use when autoscaling this Webservice.",
"minimum": 1
},
"autoscale_refresh_seconds": {
"type": "integer",
"description": "How often the autoscaler should attempt to scale this Webservice (in seconds).",
"minimum": 1
},
"autoscale_target_utilization": {
"type": "integer",
"description": "The target utilization (in percent out of 100) the autoscaler should attempt to maintain for this Webservice.",
"minimum": 1,
"maximum": 100
},
"scoring_timeout_ms": {
"type": "integer",
"description": "A timeout in ms to enforce for scoring calls to this Webservice.",
"minimum": 1
},
"replica_max_concurrent_requests": {
"type": "integer",
"description": "The number of maximum concurrent requests per replica to allow for this Webservice.",
"minimum": 1
},
"max_request_wait_time": {
"type": "integer",
"description": "The maximum amount of time a request will stay in the queue (in milliseconds) before returning a 503 error."
},
"num_replicas": {
"type": "integer",
"description": "The number of containers to allocate for this Webservice."
},
"period_seconds": {
"type": "integer",
"description": "How often (in seconds) to perform the liveness probe.",
"minimum": 1
},
"initial_delay_seconds": {
"type": "integer",
"description": "The number of seconds after the container has started before liveness probes are initiated.",
"minimum": 1
},
"timeout_seconds": {
"type": "integer",
"description": "The number of seconds after which the liveness probe times out.",
"minimum": 1
},
"success_threshold": {
"type": "integer",
"description": "The minimum consecutive successes for the liveness probe to be considered successful after having failed.",
"minimum": 1
},
"failure_threshold": {
"type": "integer",
"description": "When a Pod starts and the liveness probe fails, Kubernetes will try failureThreshold times before giving up.",
"minimum": 1
},
"namespace": {
"type": "string",
"description": "The Kubernetes namespace in which to deploy this Webservice.",
"maxLength": 63,
"pattern": "([a-z0-9\-])+"
},
"token_auth_enabled": {
"type": "boolean",
"description": "Whether to enable Token authentication for this Webservice."
}
}
}

Просмотреть файл

@ -1,3 +1,6 @@
import jsonschema
class AMLConfigurationException(Exception):
pass
@ -6,15 +9,15 @@ class AMLDeploymentException(Exception):
pass
def required_parameters_provided(parameters, keys, message="Required parameter(s) not found in your parameters file. Please provide a value for the following key(s): "):
missing_keys = []
for key in keys:
if key not in parameters:
err_msg = f"{message} {key}"
print(f"::error::{err_msg}")
missing_keys.append(key)
if len(missing_keys) > 0:
raise AMLConfigurationException(f"{message} {missing_keys}")
def validate_json(data, schema, input_name):
validator = jsonschema.Draft7Validator(schema)
errors = validator.iter_errors(data)
if len(list(errors)) > 0:
for error in errors:
print(f"::error::JSON validation error: {error}")
raise AMLConfigurationException(f"JSON validation error for '{input_name}'. Provided object does not match schema. Please check the output for more details.")
else:
print(f"::debug::JSON validation passed for '{input_name}'. Provided object does match schema.")
def get_resource_config(config, resource_config, config_name):

35
tests/test_main.py Normal file
Просмотреть файл

@ -0,0 +1,35 @@
import os
import sys
import pytest
myPath = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(myPath, "..", "code"))
from main import main
from utils import AMLConfigurationException
def test_main_no_input():
"""
Unit test to check the main function with no inputs
"""
with pytest.raises(AMLConfigurationException):
assert main()
def test_main_invalid_azure_credentials():
os.environ["INPUT_AZURE_CREDENTIALS"] = ""
with pytest.raises(AMLConfigurationException):
assert main()
def test_main_invalid_parameters_file():
os.environ["INPUT_AZURE_CREDENTIALS"] = """{
'clientId': 'test',
'clientSecret': 'test',
'subscriptionId': 'test',
'tenantId': 'test'
}"""
os.environ["INPUT_PARAMETERS_FILE"] = "wrongfile.json"
with pytest.raises(AMLConfigurationException):
assert main()

59
tests/test_utils.py Normal file
Просмотреть файл

@ -0,0 +1,59 @@
import os
import sys
import pytest
myPath = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(myPath, "..", "code"))
from utils import validate_json, AMLConfigurationException
from schemas import azure_credentials_schema
def test_validate_json_valid_inputs():
"""
Unit test to check the validate_json function with valid inputs
"""
json_object = {
"clientId": "",
"clientSecret": "",
"subscriptionId": "",
"tenantId": ""
}
schema_object = azure_credentials_schema
validate_json(
data=json_object,
schema=schema_object,
input_name="PARAMETERS_FILE"
)
def test_validate_json_invalid_json():
"""
Unit test to check the validate_json function with invalid json_object inputs
"""
json_object = {
"clientId": "",
"clientSecret": "",
"subscriptionId": ""
}
schema_object = azure_credentials_schema
with pytest.raises(AMLConfigurationException):
assert validate_json(
data=json_object,
schema=schema_object,
input_name="PARAMETERS_FILE"
)
def test_validate_json_invalid_schema():
"""
Unit test to check the validate_json function with invalid schema inputs
"""
json_object = {}
schema_object = {}
with pytest.raises(Exception):
assert validate_json(
data=json_object,
schema=schema_object,
input_name="PARAMETERS_FILE"
)