diff --git a/.github/workflows/healthcheck-app-existingVnet-Lin-Thurs.yml b/.github/workflows/healthcheck-app-existingVnet-Lin-Thurs.yml new file mode 100644 index 0000000..c8272b8 --- /dev/null +++ b/.github/workflows/healthcheck-app-existingVnet-Lin-Thurs.yml @@ -0,0 +1,37 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information, see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +#The following workflow file triggers the Existing Virtual network deployment test "test_refarch_existing.py" every Thursday with platform as "Linux" in "Japaneast" region. + +name: MATLAB Web App Server Ref Arch Azure Existing Virtual Network Health Check Test on linux in Japan East + +on: + schedule: + - cron: '0 16 * * 4' +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/checkout@v4 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + pip install -r ./healthcheck/requirements.txt + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: MWAS ref Arch Existing Virtual Network Health check test on linux in Japan East + run: | + cd healthcheck + python test_refarch_existing.py ${{ secrets.TenantId }} ${{ secrets.ClientId }} ${{ secrets.ClientSecret }} ${{ secrets.SubscriptionId }} ${{ secrets.username }} ${{ secrets.password }} ${{ secrets.ipaddress }} ${{ secrets.sslcertificate }} ${{ secrets.sslkey }} "japaneast" "Linux" "True" + diff --git a/.github/workflows/healthcheck-app-existingVnet-Win-Tue.yml b/.github/workflows/healthcheck-app-existingVnet-Win-Tue.yml new file mode 100644 index 0000000..778d9d5 --- /dev/null +++ b/.github/workflows/healthcheck-app-existingVnet-Win-Tue.yml @@ -0,0 +1,37 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information, see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +# The following workflow file triggers the Existing Virtual Network deployment test "test_refarch_existing.py" every Tuesday with platform as "Windows" in "westeurope" region. + +name: MATLAB Web App Server Ref Arch Azure Existing Virtual Network Health Check Test on Windows in West Eu + +on: + schedule: + - cron: '0 16 * * 2' +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + pip install -r ./healthcheck/requirements.txt + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: MWAS ref Arch Existing Virtual Network Health check test on Windows in West Eu + run: | + cd healthcheck + python test_refarch_existing.py ${{ secrets.TenantId }} ${{ secrets.ClientId }} ${{ secrets.ClientSecret }} ${{ secrets.SubscriptionId }} ${{ secrets.username }} ${{ secrets.password }} ${{ secrets.ipaddress }} ${{ secrets.sslcertificate }} ${{ secrets.sslkey }} "westeurope" "Windows" "True" + diff --git a/.github/workflows/healthcheck-app-newVnet-Lin-Wed.yml b/.github/workflows/healthcheck-app-newVnet-Lin-Wed.yml new file mode 100644 index 0000000..6e400a5 --- /dev/null +++ b/.github/workflows/healthcheck-app-newVnet-Lin-Wed.yml @@ -0,0 +1,37 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information, see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +# The following workflow file triggers the New Virtual Network deployment test "test_refarch_new" every Wednesday with platform as "Linux" in "westus" region. + +name: MATLAB Web App Server Ref Arch Azure Health Check Test on Linux in West US + +on: + schedule: + - cron: '0 16 * * 3' +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + pip install -r ./healthcheck/requirements.txt + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Web App ref Arch New Virtual Network Health check test on Linux in West US + run: | + cd healthcheck + python test_refarch_existing.py ${{ secrets.TenantId }} ${{ secrets.ClientId }} ${{ secrets.ClientSecret }} ${{ secrets.SubscriptionId }} ${{ secrets.username }} ${{ secrets.password }} ${{ secrets.ipaddress }} ${{ secrets.sslcertificate }} ${{ secrets.sslkey }} "westus" "Linux" "False" + diff --git a/.github/workflows/healthcheck-app-newVnet-Win-Mon.yml b/.github/workflows/healthcheck-app-newVnet-Win-Mon.yml new file mode 100644 index 0000000..dcde85a --- /dev/null +++ b/.github/workflows/healthcheck-app-newVnet-Win-Mon.yml @@ -0,0 +1,38 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information, see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +# The following workflow file triggers the New Virtual Network deployment test "test_refarch_new" every Monday with platform as "Windows" in "eastus" region. + +name: MATLAB Web App Server Ref Arch Azure Health Check Test on Windows in East US region + +on: + push: + schedule: + - cron: '0 16 * * 1' +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Set up Python 3.8 + uses: actions/setup-python@v4 + with: + python-version: 3.8 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + pip install -r ./healthcheck/requirements.txt + + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Web App ref Arch New Virtual Network Health check test on Windows in East US region + run: | + cd healthcheck + python test_refarch_existing.py ${{ secrets.TenantId }} ${{ secrets.ClientId }} ${{ secrets.ClientSecret }} ${{ secrets.SubscriptionId }} ${{ secrets.username }} ${{ secrets.password }} ${{ secrets.ipaddress }} ${{ secrets.sslcertificate }} ${{ secrets.sslkey }} "eastus" "Windows" "False" + diff --git a/healthcheck/README.md b/healthcheck/README.md new file mode 100644 index 0000000..6796cfb --- /dev/null +++ b/healthcheck/README.md @@ -0,0 +1 @@ +This section of repository is to contain the tests for health check of published MATLAB Web App Server Ref Arch Azure health check. It will be scheduled to run everyday nightly and confirm the deployment can be done successfully, and links that we published to github are in good health. \ No newline at end of file diff --git a/healthcheck/requirements.txt b/healthcheck/requirements.txt new file mode 100644 index 0000000..bf596d5 --- /dev/null +++ b/healthcheck/requirements.txt @@ -0,0 +1,8 @@ +azure-functions +azure-mgmt-resource==8.0.1 +azure-mgmt-marketplaceordering==0.1.0 +azure-mgmt-compute==4.6.2 +azure-mgmt-network==10.2.0 +azure-common==1.1.24 +adal==1.2.2 +msrestazure==0.6.2 \ No newline at end of file diff --git a/healthcheck/test_refarch_existing.py b/healthcheck/test_refarch_existing.py new file mode 100644 index 0000000..20f427a --- /dev/null +++ b/healthcheck/test_refarch_existing.py @@ -0,0 +1,107 @@ +import unittest +import os +import time +import re +import requests +import random +import sys +from datetime import date +import datetime + +import testtools.AzureAuthentication as AzureAuth +import testtools.deploy as DeployOp +from azure.mgmt.network import NetworkManagementClient + +def main(tenant_id_arg, client_id_arg, client_secret_arg, subscription_id_arg, username, password, ipAddress, sslCertificate, sslPrivateKey, location_arg, platform_arg, existingVPC): + + # Deploy template + # Reference architecture in production. + ref_arch_name = 'matlab-web-app-server-on-azure' + + # Common parameters for template deployment. + tenant_id = tenant_id_arg + client_id = client_id_arg + client_secret = client_secret_arg + credentials = AzureAuth.authenticate_client_key(tenant_id, client_id, client_secret) + subscription_id = subscription_id_arg + location = location_arg + + if existingVPC: + # Subnets & virtual network info + subnets_cidr = ['10.0.0.0/24'] + vnet_cidr = '10.0.0.0/16' + # Resource group where virtual network is created + resource_name_vnet = 'vnet_resource_group' + # Deploy a resource group with a virtual network and specified number of subnets + try: + subnet_name, vnet_name = DeployOp.create_vnet(credentials, + subscription_id, + location, + subnets_cidr, + resource_name_vnet, + vnet_cidr) + except Exception as e: + raise(e) + print(subnet_name[0]) + # Parameters for deployment + parameters = { + "IP Addresses Permitted to Remote into Server VM in CIDR Notation": "0.0.0.0/0", + "IP Addresses Allowed to Access MATLAB Web App Server Apps Home Page in CIDR Notation": ipAddress, + "Base64 Encoded SSL Certificate": sslCertificate, + "Base64 Encoded SSL Private Key": sslPrivateKey, + "Username to Remote into Server VM and Network License Manager Web Interface": username, + "Password to Remote into Server VM and Network License Manager Web Interface": password + } + + if existingVPC: + parameters.update({ + "Deploy to New or Existing Virtual Network": "existing", + "Name of Virtual Network Where MATLAB Web App Server Will Be Deployed": vnet_name, + "Virtual Network CIDR Range": vnet_cidr, + "Name of Subnet for MATLAB Web App Server": subnet_name[0], + "Server Subnet CIDR Range": subnets_cidr[0], + "Resource Group Name Of Virtual Network": resource_name_vnet, + "Operating System": platform_arg + }) + + print(parameters) + + # Find latest MATLAB release from Github page and get template json path. + res = requests.get( + f"https://github.com/mathworks-ref-arch/{ref_arch_name}/tree/main/releases/" + ) + latest_releases = [re.findall("releases/(R\d{4}[ab]\\b)", res.text)[-1], re.findall("releases/(R\d{4}[ab]\\b)", res.text)[-2]] + for i in range(2): + matlab_release = latest_releases[i] + print("Testing Health Check Release: " + matlab_release + "\n") + github_base_dir = "https://raw.githubusercontent.com/mathworks-ref-arch" + jsonpath = f"{matlab_release}/templates/mainTemplate.json" + template_name = f"{github_base_dir}/{ref_arch_name}/main/releases/{jsonpath}" + resource_group_name = "webapp-refarch-health-check-" + matlab_release + date.today().strftime('%m-%d-%Y') + str(random.randint(1,101)) + ct = datetime.datetime.now() + print("Date time before deployment of stack:-", ct) + + try: + deployment_result = DeployOp.deploy_webapp_template(credentials, + subscription_id, + resource_group_name, + location, + ref_arch_name, + template_name, + parameters + ) + except Exception as e: + raise(e) + + if existingVPC: + # Delete the deployment which is deployed using existing virtual network + deployment_deletion = DeployOp.delete_resourcegroup(credentials, subscription_id, resource_group_name) + print("Deleted the deployment which is deployed using existing virtual network") + # Wait for above deployment deletion + time.sleep(900) + # Delete deployment with virtual network + DeployOp.delete_resourcegroup(credentials, subscription_id, resource_name_vnet) + print("Deleted the deployment which contains the virtual network") + +if __name__ == '__main__': + main(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6], sys.argv[7], sys.argv[8], sys.argv[9], sys.argv[10], sys.argv[11], sys.argv[12]) diff --git a/healthcheck/testtools/AzureAuthentication.py b/healthcheck/testtools/AzureAuthentication.py new file mode 100644 index 0000000..831029e --- /dev/null +++ b/healthcheck/testtools/AzureAuthentication.py @@ -0,0 +1,19 @@ +from azure.common import credentials +from azure.common.credentials import ServicePrincipalCredentials +from msrestazure.azure_active_directory import AADTokenCredentials +import adal + +def authenticate_client_key(tenant_id, client_id, client_secret): + + """ + Authenticate using service principal w/ key. + """ + authority_host_uri = 'https://login.microsoftonline.com' + authority_uri = authority_host_uri + '/' + tenant_id + resource_uri = 'https://management.core.windows.net/' + + context = adal.AuthenticationContext(authority_uri, api_version=None) + mgmt_token = context.acquire_token_with_client_credentials(resource_uri, client_id, client_secret) + credentials = AADTokenCredentials(mgmt_token, client_id) + + return credentials \ No newline at end of file diff --git a/healthcheck/testtools/__init__.py b/healthcheck/testtools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/healthcheck/testtools/deploy.py b/healthcheck/testtools/deploy.py new file mode 100644 index 0000000..b6f8b21 --- /dev/null +++ b/healthcheck/testtools/deploy.py @@ -0,0 +1,125 @@ +import urllib.request, json +import azure.functions as func + +from azure.mgmt.resource.resources.models import DeploymentMode +from azure.mgmt.compute import ComputeManagementClient +from azure.common import credentials +from azure.common.credentials import ServicePrincipalCredentials +from msrestazure.azure_active_directory import AADTokenCredentials +from azure.mgmt.resource.resources.models import DeploymentProperties +from azure.mgmt.resource.resources.models import DeploymentMode +from azure.mgmt.resource.resources.models import TemplateLink +import testtools.getResourceClient as getResourceClient +from azure.mgmt.network import NetworkManagementClient + +def deploy_webapp_template(credentials, + subscription_id, + resource_group_name, + location, + ref_arch_name, + template_name, + resource_group_params): + + with urllib.request.urlopen(f'{template_name}') as url: + template = json.loads(url.read().decode()) + + parameters = {k: {'value': v} for k, v in resource_group_params.items()} + + deployment_properties = { + 'mode': DeploymentMode.incremental, + 'template': template, + 'parameters': parameters + } + + resource_client = getResourceClient.get_resource_client(credentials, subscription_id) + + # Create resource group... + resource_client.resource_groups.create_or_update( + resource_group_name, + {'location': location} + ) + + print("Beginning the deployment... \n\n") + # Deploy template. + + deployment = resource_client.deployments.create_or_update( + resource_group_name, + f'{ref_arch_name}-deployment', + deployment_properties + ) + + # Block because of VM quotas. + deployment.wait() + print(deployment) + + #extract output + deployment_result = resource_client.deployments.get(resource_group_name, f'{ref_arch_name}-deployment') + print(deployment_result) + print("Done with the deployment... \n\n") + return deployment_result + +def create_vnet(credentials, + subscription_id, + location, + subnets_cidr, + resource_name_vnet, + vnet_cidr): + + resource_client = getResourceClient.get_resource_client(credentials, subscription_id) + vnet_name = 'my_vnet' + + # Create resource group + print("Creating a resource group with a virtual network... \n") + resource_client.resource_groups.create_or_update( + resource_name_vnet, + {'location': location} + ) + network_client = NetworkManagementClient(credentials, subscription_id) + + print("Resource group created...\n") + + # Create virtual network + async_vnet_creation = network_client.virtual_networks.create_or_update( + resource_name_vnet, + vnet_name, + { + 'address_space': { + 'address_prefixes': [vnet_cidr] + }, + 'location': location + + } + ) + + # Wait for the virtual network creation + async_vnet_creation.wait() + + print("Added a virtual network... \n") + + # Array to store the names of the subnets created + subnet_names = [] + + for i in range(1, len(subnets_cidr) + 1): + + # Create Subnet + subnet_name = "subnet" + str(i) + async_subnet_creation = network_client.subnets.create_or_update( + resource_name_vnet, + vnet_name, + subnet_name, + {'address_prefix': subnets_cidr[i-1]} + ) + subnet_info = async_subnet_creation.result() + # Add created subnet name to subnet_names array + subnet_names.append(subnet_info.name) + + print("Added " + str(len(subnets_cidr)) + " subnets") + # Return the names of subnets created and name of the virtual network + return subnet_names, vnet_name + +def delete_resourcegroup(credentials, subscription_id, resource_group_name) : + resource_client = getResourceClient.get_resource_client(credentials, subscription_id) + print("Deleting the deployment... \n\n") + deployment_deletion = resource_client.resource_groups.delete(resource_group_name) + print(deployment_deletion) + #return deployment_deletion \ No newline at end of file diff --git a/healthcheck/testtools/getResourceClient.py b/healthcheck/testtools/getResourceClient.py new file mode 100644 index 0000000..d5c410b --- /dev/null +++ b/healthcheck/testtools/getResourceClient.py @@ -0,0 +1,5 @@ +from azure.mgmt.resource import ResourceManagementClient + +def get_resource_client(credentials, subscription_id) : + resource_client = ResourceManagementClient(credentials, subscription_id) + return resource_client \ No newline at end of file