diff --git a/.github/ISSUE_TEMPLATE/load_test_report.md b/.github/ISSUE_TEMPLATE/load_test_report.md new file mode 100644 index 00000000..f538257c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/load_test_report.md @@ -0,0 +1,8 @@ +--- +title: Load test failure! +assignees: + - DennisvdLaar +labels: + - bug +--- +A load test failure has occurred. Please access the Azure Load Test portal to learn more about this latest run. \ No newline at end of file diff --git a/.github/workflows/Deploy_IaC.yml b/.github/workflows/Deploy_IaC.yml new file mode 100644 index 00000000..4ca496e4 --- /dev/null +++ b/.github/workflows/Deploy_IaC.yml @@ -0,0 +1,39 @@ +on: + workflow_dispatch: + inputs: + targetEnv: + description: 'Environment (dev, test, prod)' + required: true + type: string + +env: + targetEnv: ${{ inputs.targetEnv }} + +name: Azure Bicep +jobs: + build-and-deploy: + runs-on: ubuntu-latest + permissions: + contents: read + pages: write + id-token: write + steps: + # Checkout code + - uses: actions/checkout@main + + # Log into Azure + - uses: azure/login@v1 + with: + client-id: ${{ secrets.AZURE_CLIENT_ID }} + tenant-id: ${{ secrets.AZURE_TENANT_ID }} + subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + enable-AzPSSession: true + + # Deploy ARM template + - name: Run ARM deploy + uses: azure/arm-deploy@v1 + with: + subscriptionId: ${{ secrets.AZURE_SUBSCRIPTION_ID }} + resourceGroupName: ${{ secrets.AZURE_RG }} + template: ./InfrastructureAsCode/main.bicep + parameters: environment=${{ env.targetEnv }} \ No newline at end of file diff --git a/.github/workflows/LoadTest.yml b/.github/workflows/LoadTest.yml new file mode 100644 index 00000000..26c9cfc8 --- /dev/null +++ b/.github/workflows/LoadTest.yml @@ -0,0 +1,52 @@ +name: Load Test Trigger + +on: + push: + branches: + - main + workflow_dispatch: + +env: + AZURE_WEBAPP_NAME: "yjgubz5iu6gby-dev" # set this to your application's name + LOAD_TEST_RESOURCE: "LoadTestDvdL" # set this to your Azure Load Test resource's name + LOAD_TEST_RESOURCE_GROUP: "RG-Deploy" # set this to the resource group you've used for this training + +jobs: + loadTest: + name: Load Test + runs-on: ubuntu-latest + steps: + - name: Checkout GitHub Actions + uses: actions/checkout@v2 + + - name: Login to Azure + uses: azure/login@v1 + continue-on-error: false + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: 'Azure Load Testing' + uses: azure/load-testing@v1 + with: + loadTestConfigFile: 'LoadTestConfig.yml' + loadTestResource: ${{ env.LOAD_TEST_RESOURCE }} + resourceGroup: ${{ env.LOAD_TEST_RESOURCE_GROUP }} + env: | + [ + { + "name": "webapp", + "value": "${{ env.AZURE_WEBAPP_NAME }}.azurewebsites.net" + } + ] + + - uses: JasonEtco/create-an-issue@v2.9.1 + if: ${{ failure() }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + filename: .github/ISSUE_TEMPLATE/load_test_report.md + + - uses: actions/upload-artifact@v2 + with: + name: loadTestResults + path: ${{ github.workspace }}/loadTest \ No newline at end of file diff --git a/.github/workflows/appDeploy.yml b/.github/workflows/appDeploy.yml new file mode 100644 index 00000000..eccc97a7 --- /dev/null +++ b/.github/workflows/appDeploy.yml @@ -0,0 +1,135 @@ +name: .NET CI + +env: + registryName: yjgubz5iu6gbympnpreg.azurecr.io + repositoryName: techboost/dotnetcoreapp + dockerFolderPath: ./Application/src/RazorPagesTestSample + tag: ${{github.run_number}} + +on: + push: + branches: [ main ] + paths: Application/** + pull_request: + branches: [ main ] + paths: Application/** + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 6.0 + + - name: Restore dependencies + run: dotnet restore ./Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + - name: Build + run: dotnet build --no-restore ./Application/src/RazorPagesTestSample/RazorPagesTestSample.csproj + - name: Test + run: dotnet test --no-build --verbosity normal ./Application/tests/RazorPagesTestSample.Tests/RazorPagesTestSample.Tests.csproj + - uses: actions/github-script@v6 + if: failure() + with: + github-token: ${{secrets.GITHUB_TOKEN}} + script: | + let body = "${{ env.build_name }} Workflow Failure \n Build Number: ${{ github.run_number }} \n Build Log: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} \n SHA: [${{ github.sha }}](https://github.com/${{ github.repository }}/commit/${{ github.sha }}) \n"; + github.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: "${{ env.build_name }} Workflow ${{ github.run_number }} Failed! ", + body: body + }); + + dockerBuildPush: + runs-on: ubuntu-latest + needs: build + + steps: + - uses: actions/checkout@v3 + + - name: Docker Login + # You may pin to the exact commit or the version. + # uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c + uses: docker/login-action@v1.9.0 + with: + # Server address of Docker registry. If not set then will default to Docker Hub + registry: ${{ secrets.ACR_LOGIN_SERVER }} + # Username used to log against the Docker registry + username: ${{ secrets.ACR_USERNAME }} + # Password or personal access token used to log against the Docker registry + password: ${{ secrets.ACR_PASSWORD }} + # Log out from the Docker registry at the end of a job + logout: true + + - name: Docker Build + run: docker build -t $registryName/$repositoryName:$tag --build-arg build_version=$tag $dockerFolderPath + + - name: Docker Push + run: docker push $registryName/$repositoryName:$tag + + deploy-to-dev: + + runs-on: ubuntu-latest + needs: dockerBuildPush + environment: + name: dev + url: https://yjgubz5iu6gby-dev.azurewebsites.net/ + + steps: + - name: 'Login via Azure CLI' + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - uses: azure/webapps-deploy@v2 + with: + app-name: 'yjgubz5iu6gby-dev' + images: yjgubz5iu6gbympnpreg.azurecr.io/techboost/dotnetcoreapp:${{github.run_number}} + + deploy-to-test: + + runs-on: ubuntu-latest + needs: deploy-to-dev + environment: + name: test + url: https://yjgubz5iu6gby-test.azurewebsites.net/ + + steps: + - uses: actions/checkout@v3 + + - name: 'Login via Azure CLI' + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - uses: azure/webapps-deploy@v2 + with: + app-name: 'yjgubz5iu6gby-test' + images: yjgubz5iu6gbympnpreg.azurecr.io/techboost/dotnetcoreapp:${{github.run_number}} + + deploy-to-prod: + + runs-on: ubuntu-latest + needs: deploy-to-test + environment: + name: prod + url: https://yjgubz5iu6gby-prod.azurewebsites.net/ + + steps: + - uses: actions/checkout@v3 + + - name: 'Login via Azure CLI' + uses: azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - uses: azure/webapps-deploy@v2 + with: + app-name: 'yjgubz5iu6gby-prod' + images: yjgubz5iu6gbympnpreg.azurecr.io/techboost/dotnetcoreapp:${{github.run_number}} \ No newline at end of file diff --git a/.github/workflows/first-workflow.yml b/.github/workflows/first-workflow.yml new file mode 100644 index 00000000..7cd89110 --- /dev/null +++ b/.github/workflows/first-workflow.yml @@ -0,0 +1,52 @@ +# The name of the job is what will display on the GitHub repository in the Actions tab. +name: First Workflow + +# The 'on' section tells GitHub under what conditions we want to run this workflow. +# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows +# Common scenarios include: + # workflow-dispatch (manual execution) + # issues + # push + # pull_request + # schedule +on: + workflow_dispatch: + issues: + types: [opened] + +# This section covers the work to perform. +# We include one or more jobs in this section. +jobs: + # Each individual job will include details like execution order, + # pre-requisite jobs, and execution platform. + job1: + # We can run jobs on GitHub hosted VM runners in Windows, Ubuntu, and Mac OS. + # We can also run jobs on self-hosted hardware. + runs-on: ubuntu-latest + + # Each job contains one or more steps. A step needs to have at least a name and a command. + steps: + - name: Step one + # The 'run' command executes a shell or command script. Because this is Ubuntu, the + # default run command will be /bin/bash + run: echo "Log from step one" + + - name: Step two + run: echo "Log from step two" + + job2: + needs: [job1] + #Use a GitHub Action named: CowSays + runs-on: ubuntu-latest + + steps: + - name: Cowsays + # You may pin to the exact commit or the version. + # uses: mscoutermarsh/cowsays-action@822c8424f7ebc1f4c8b86b0bcb11e4051b7f42e2 + uses: mscoutermarsh/cowsays-action@v1 + with: + # What does the cow say? + text: Ready for prod–ship it! # optional, default is Ship it!!!!! + # Color of your cow + color: Magenta # optional, default is white + diff --git a/Application/src/RazorPagesTestSample/Data/Message.cs b/Application/src/RazorPagesTestSample/Data/Message.cs index ea99cbd6..59f24395 100644 --- a/Application/src/RazorPagesTestSample/Data/Message.cs +++ b/Application/src/RazorPagesTestSample/Data/Message.cs @@ -9,7 +9,7 @@ public class Message [Required] [DataType(DataType.Text)] - [StringLength(200, ErrorMessage = "There's a 200 character limit on messages. Please shorten your message.")] + [StringLength(250, ErrorMessage = "There's a 250 character limit on messages. Please shorten your message.")] public string Text { get; set; } } #endregion diff --git a/LoadTestConfig.yml b/LoadTestConfig.yml new file mode 100644 index 00000000..f98f16cd --- /dev/null +++ b/LoadTestConfig.yml @@ -0,0 +1,8 @@ +version: v0.1 +testName: mpnploadtest +testPlan: LoadTestScript.jmx +description: 'CI/CD Test Run for MP&P load test' +engineInstances: 1 +failureCriteria: + - avg(response_time_ms) > 600 + - percentage(error) > 10 \ No newline at end of file diff --git a/LoadTestScript.jmx b/LoadTestScript.jmx new file mode 100644 index 00000000..6a693c18 --- /dev/null +++ b/LoadTestScript.jmx @@ -0,0 +1,688 @@ + + + + + false + false + false + + + + site + ${__BeanShell( System.getenv("webapp") )} + = + + + numUsers + ${__BeanShell( System.getenv("numUsers") )} + = + + + + + + + continue + + 20 + false + + 30 + 30 + false + false + + + true + + + + + + + ${site} + 80 + https + + + + + + false + + + + 443 + / + GET + true + false + true + false + false + false + false + 6 + false + 0 + + + + + + Sec-Fetch-Mode + navigate + + + Referer + https://${site}/ + + + Sec-Fetch-Site + same-origin + + + Accept-Language + en-US,en;q=0.5 + + + Accept + text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + + + Upgrade-Insecure-Requests + 1 + + + Accept-Encoding + gzip, deflate, br + + + User-Agent + Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0 + + + Sec-Fetch-Dest + document + + + + + + token + input[name=__RequestVerificationToken] + value + + false + + + + + + + false + + + + true + Message.Text + This is a test message! + = + true + text/plain + + + false + __RequestVerificationToken + ${token} + = + true + + + + 443 + utf-8 + /?handler=AddMessage + POST + true + false + true + false + false + false + false + 6 + false + 0 + Detected the start of a redirect chain + + + + + + Sec-Fetch-Mode + navigate + + + Referer + https://${site}/ + + + Sec-Fetch-Site + same-origin + + + Accept-Language + en-US,en;q=0.5 + + + Origin + https://${site} + + + Sec-Fetch-User + ?1 + + + Accept + text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + + + Upgrade-Insecure-Requests + 1 + + + Content-Type + application/x-www-form-urlencoded + + + Accept-Encoding + gzip, deflate, br + + + User-Agent + Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0 + + + Sec-Fetch-Dest + document + + + + + + + false + + + + 443 + utf-8 + / + GET + true + false + true + false + false + false + false + 6 + false + 0 + + + + + + Sec-Fetch-Mode + navigate + + + Referer + https://${site}/ + + + Sec-Fetch-Site + same-origin + + + Accept-Language + en-US,en;q=0.5 + + + Sec-Fetch-User + ?1 + + + Accept + text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + + + Upgrade-Insecure-Requests + 1 + + + Accept-Encoding + gzip, deflate, br + + + User-Agent + Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0 + + + Sec-Fetch-Dest + document + + + + + + + false + + + + false + __RequestVerificationToken + ${token} + = + + + + 443 + utf-8 + /?id=8&handler=DeleteMessage + POST + true + false + true + false + false + false + false + 6 + false + 0 + Detected the start of a redirect chain + + + + + + Sec-Fetch-Mode + navigate + + + Referer + https://${site}/ + + + Sec-Fetch-Site + same-origin + + + Accept-Language + en-US,en;q=0.5 + + + Origin + https://${site} + + + Sec-Fetch-User + ?1 + + + Accept + text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + + + Upgrade-Insecure-Requests + 1 + + + Content-Type + application/x-www-form-urlencoded + + + Accept-Encoding + gzip, deflate, br + + + User-Agent + Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0 + + + Sec-Fetch-Dest + document + + + + + + + false + + + + 443 + utf-8 + / + GET + true + false + true + false + false + false + false + 6 + false + 0 + + + + + + Sec-Fetch-Mode + navigate + + + Referer + https://${site}/ + + + Sec-Fetch-Site + same-origin + + + Accept-Language + en-US,en;q=0.5 + + + Sec-Fetch-User + ?1 + + + Accept + text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + + + Upgrade-Insecure-Requests + 1 + + + Accept-Encoding + gzip, deflate, br + + + User-Agent + Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0 + + + Sec-Fetch-Dest + document + + + + + + + false + + + + false + __RequestVerificationToken + ${token} + = + + + + 443 + utf-8 + /?handler=AnalyzeMessages + POST + true + false + true + false + false + false + false + 6 + false + 0 + Detected the start of a redirect chain + + + + + + Sec-Fetch-Mode + navigate + + + Referer + https://${site}/ + + + Sec-Fetch-Site + same-origin + + + Accept-Language + en-US,en;q=0.5 + + + Origin + https://${site} + + + Sec-Fetch-User + ?1 + + + Accept + text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + + + Upgrade-Insecure-Requests + 1 + + + Content-Type + application/x-www-form-urlencoded + + + Accept-Encoding + gzip, deflate, br + + + User-Agent + Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0 + + + Sec-Fetch-Dest + document + + + + + + + false + + + + 443 + utf-8 + / + GET + true + false + true + false + false + false + false + 6 + false + 0 + + + + + + Sec-Fetch-Mode + navigate + + + Referer + https://${site}/ + + + Sec-Fetch-Site + same-origin + + + Accept-Language + en-US,en;q=0.5 + + + Sec-Fetch-User + ?1 + + + Accept + text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 + + + Upgrade-Insecure-Requests + 1 + + + Accept-Encoding + gzip, deflate, br + + + User-Agent + Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/118.0 + + + Sec-Fetch-Dest + document + + + + + + + + + false + false + + + + + 8888 + + + true + 0 + false + + false + true + true + true + false + 0 + + singlepage + + false + false + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + false + + saveConfig + + + true + true + true + + true + true + true + true + false + true + true + false + false + false + true + false + false + false + true + 0 + true + true + true + true + true + true + + + + + + + + \ No newline at end of file