Упражнение. Добавление этапа тестирования в конвейер

Завершено

Команда обеспечения безопасности компании по продаже игрушек попросила вас убедиться в том, что веб-сайт доступен только по протоколу HTTPS. В этом упражнении вы настроите конвейер для выполнения теста дыма, который проверяет требование команды безопасности.

В процессе вы:

  • Добавьте в репозиторий сценарий теста.
  • Обновите определение конвейера и добавьте этап тестирования.
  • Запустите конвейер и понаблюдайте за сбоем теста.
  • Исправьте файл Bicep и понаблюдайте за успешным выполнением конвейера.

Добавление сценария теста

Здесь вы добавите тестовый скрипт, чтобы убедиться, что веб-сайт доступен при использовании HTTPS и недоступен при использовании небезопасного протокола HTTP.

  1. В Visual Studio Code создайте новый файл Website.Tests.ps1 в папке deploy.

    Снимок экрана: Visual Studio Code Explorer с папкой развертывания и тестовым файлом.

  2. Вставьте в файл приведенный ниже тестовый код.

    param(
      [Parameter(Mandatory)]
      [ValidateNotNullOrEmpty()]
      [string] $HostName
    )
    
    Describe 'Toy Website' {
    
        It 'Serves pages over HTTPS' {
          $request = [System.Net.WebRequest]::Create("https://$HostName/")
          $request.AllowAutoRedirect = $false
          $request.GetResponse().StatusCode |
            Should -Be 200 -Because "the website requires HTTPS"
        }
    
        It 'Does not serves pages over HTTP' {
          $request = [System.Net.WebRequest]::Create("http://$HostName/")
          $request.AllowAutoRedirect = $false
          $request.GetResponse().StatusCode | 
            Should -BeGreaterOrEqual 300 -Because "HTTP is not secure"
        }
    
    }
    

    Этот код представляет собой тестовый файл Pester. Для этого требуется параметр с именем $HostName. Он выполняет два теста для имени узла:

    • Попробуйте подключиться к веб-сайту по протоколу HTTPS. Тест считается пройденным, если сервер возвращает код состояния HTTP-ответа в диапазоне от 200 до 299, что говорит об успешном подключении.
    • Попробуйте подключиться к веб-сайту по протоколу HTTP. Тест считается пройденным, если сервер возвращает код состояния HTTP-ответа от 300.

    Для целей этого упражнения необязательно полностью понимать тестовый файл и принцип его работы. Мы предоставим ссылки в сводке, чтобы узнать больше, если вы хотите.

Публикация выходных данных файла Bicep в качестве выходной переменной этапа

Сценарий теста, созданный на предыдущих шагах, требует имя узла для проверки. Файл Bicep уже содержит выходные данные, однако прежде чем использовать его в тестах проверки сборки, необходимо опубликовать его в качестве выходной переменной этапа.

  1. В Visual Studio Code откройте файл azure-pipelines.yml в папке deploy.

  2. На этапе развертывания обновите шаг развертывания, чтобы передать выходные данные в переменную:

    - task: AzureResourceManagerTemplateDeployment@3
      name: DeployBicepFile
      displayName: Deploy Bicep file
      inputs:
        connectedServiceName: $(ServiceConnectionName)
        deploymentName: $(Build.BuildNumber)
        location: $(deploymentDefaultLocation)
        resourceGroupName: $(ResourceGroupName)
        csmFile: deploy/main.bicep
        overrideParameters: >
          -environmentType $(EnvironmentType)
        deploymentOutputs: deploymentOutputs
    

    Теперь процесс развертывания использует ту же задачу, что и раньше, но все выходные данные развертываний сохраняются в переменной конвейера с именем deploymentOutputs. Выходная переменная создается в формате JSON.

  3. Чтобы преобразовать выходные данные из формата JSON в переменные конвейера, добавьте следующий шаг скрипта под шагом развертывания:

    - bash: |
        echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
      name: SaveDeploymentOutputs
      displayName: Save deployment outputs into variables
      env:
        DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    

    Если развертывание завершается успешно, скрипт получает доступ к значению выходных данных каждого развертывания Bicep. Скрипт использует jq средство для доступа к соответствующей части выходных данных JSON. Затем значение публикуется в выходной переменной этапа с тем же именем, что и выходные данные развертывания Bicep.

    Примечание.

    Pester и JQ предварительно установлены на агентах Microsoft для Azure Pipelines. Для их использования в шаге скрипта ничего делать не нужно.

  4. Сохраните файл.

Добавление этапа тестирования состояния в конвейер

Теперь можно добавить этап тестирования состояния, на котором выполняются тесты.

  1. В нижней части файла добавьте следующее определение этапа тестирования состояния.

    - stage: SmokeTest
      jobs:
      - job: SmokeTest
        displayName: Smoke test
        variables:
          appServiceAppHostName: $[ stageDependencies.Deploy.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ]
    

    Этот код определяет этап и задание. Он также создает переменную в задании с именем appServiceAppHostName. Эта переменная принимает значение из выходной переменной, созданной в предыдущем разделе.

  2. В нижней части файла добавьте следующее определение шага на этапе тестирования состояния.

    steps:
      - task: PowerShell@2
        name: RunSmokeTests
        displayName: Run smoke tests
        inputs:
          targetType: inline
          script: |
            $container = New-PesterContainer `
              -Path 'deploy/Website.Tests.ps1' `
              -Data @{ HostName = '$(appServiceAppHostName)' }
            Invoke-Pester `
              -Container $container `
              -CI
    

    На этом шаге выполняется сценарий PowerShell для тестирования сценария, написанного ранее, с помощью средства тестирования Pester.

  3. В нижней части файла добавьте следующее определение шага на этапе тестирования состояния.

    - task: PublishTestResults@2
      name: PublishTestResults
      displayName: Publish test results
      condition: always()
      inputs:
        testResultsFormat: NUnit
        testResultsFiles: 'testResults.xml'
    

    Этот шаг принимает файл результатов теста, который создает Pester, и публикует его как результаты теста конвейера. Вы увидите, как отображаются результаты в ближайшее время.

    Обратите внимание, что определение шага включает condition: always(). Это условие означает, что Azure Pipelines должен всегда публиковать результаты теста, даже если на предыдущем шаге произошел сбой. Это условие важно, так как любой неудачный тест приведет к сбою тестового шага, и обычно конвейер останавливается после сбоя.

  4. Сохраните файл.

Проверка и фиксация определения конвейера

  1. Убедитесь, что файл azure-pipelines.yml соответствует следующему коду:

    trigger:
      batch: true
      branches:
        include:
        - main
    
    pool:
      vmImage: ubuntu-latest
    
    variables:
      - name: deploymentDefaultLocation
        value: westus3
    
    stages:
    
    - stage: Lint
      jobs:
      - job: LintCode
        displayName: Lint code
        steps:
          - script: |
              az bicep build --file deploy/main.bicep
            name: LintBicepCode
            displayName: Run Bicep linter
    
    - stage: Validate
      jobs:
      - job: ValidateBicepCode
        displayName: Validate Bicep code
        steps:
          - task: AzureResourceManagerTemplateDeployment@3
            name: RunPreflightValidation
            displayName: Run preflight validation
            inputs:
              connectedServiceName: $(ServiceConnectionName)
              location: $(deploymentDefaultLocation)
              deploymentMode: Validation
              resourceGroupName: $(ResourceGroupName)
              csmFile: deploy/main.bicep
              overrideParameters: >
                -environmentType $(EnvironmentType)
    
    - stage: Preview
      jobs:
      - job: PreviewAzureChanges
        displayName: Preview Azure changes
        steps:
          - task: AzureCLI@2
            name: RunWhatIf
            displayName: Run what-if
            inputs:
              azureSubscription: $(ServiceConnectionName)
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: |
                az deployment group what-if \
                  --resource-group $(ResourceGroupName) \
                  --template-file deploy/main.bicep \
                  --parameters environmentType=$(EnvironmentType)
    
    - stage: Deploy
      jobs:
      - deployment: DeployWebsite
        displayName: Deploy website
        environment: Website
        strategy:
          runOnce:
            deploy:
              steps:
                - checkout: self
                - task: AzureResourceManagerTemplateDeployment@3
                  name: DeployBicepFile
                  displayName: Deploy Bicep file
                  inputs:
                    connectedServiceName: $(ServiceConnectionName)
                    deploymentName: $(Build.BuildNumber)
                    location: $(deploymentDefaultLocation)
                    resourceGroupName: $(ResourceGroupName)
                    csmFile: deploy/main.bicep
                    overrideParameters: >
                      -environmentType $(EnvironmentType)
                    deploymentOutputs: deploymentOutputs
    
                - bash: |
                    echo "##vso[task.setvariable variable=appServiceAppHostName;isOutput=true]$(echo $DEPLOYMENT_OUTPUTS | jq -r '.appServiceAppHostName.value')"
                  name: SaveDeploymentOutputs
                  displayName: Save deployment outputs into variables
                  env:
                    DEPLOYMENT_OUTPUTS: $(deploymentOutputs)
    
    - stage: SmokeTest
      jobs:
      - job: SmokeTest
        displayName: Smoke test
        variables:
          appServiceAppHostName: $[ stageDependencies.Deploy.DeployWebsite.outputs['DeployWebsite.SaveDeploymentOutputs.appServiceAppHostName'] ]
        steps:
          - task: PowerShell@2
            name: RunSmokeTests
            displayName: Run smoke tests
            inputs:
              targetType: inline
              script: |
                $container = New-PesterContainer `
                  -Path 'deploy/Website.Tests.ps1' `
                  -Data @{ HostName = '$(appServiceAppHostName)' }
                Invoke-Pester `
                  -Container $container `
                  -CI
    
          - task: PublishTestResults@2
            name: PublishTestResults
            displayName: Publish test results
            condition: always()
            inputs:
              testResultsFormat: NUnit
              testResultsFiles: 'testResults.xml'
    

    Если нет, обновите его в соответствии с этим примером, а затем сохраните.

  2. Зафиксируйте и отправьте изменения в репозиторий Git, выполнив следующие команды в терминале Visual Studio Code:

    git add .
    git commit -m "Add test stage"
    git push
    

Запуск конвейера и проверка результата теста

  1. В браузере перейдите к конвейеру.

  2. Выберите последнее выполнение конвейера.

    Подождите, пока конвейер завершит этапы анализа кода, проверки и предварительного просмотра. Хотя Azure Pipelines автоматически обновляет страницу с учетом последнего состояния, рекомендуется периодически обновлять страницу.

  3. Нажмите кнопку Обзор и выберите Утвердить.

    Дождитесь завершения выполнения конвейера.

  4. Обратите внимание, что этап развертывания успешно завершен. Этап тестирования состояния завершается ошибкой.

    Снимок экрана: интерфейс Azure DevOps с этапами выполнения конвейера. Этап тестирования состояния сообщает о сбое.

  5. Перейдите на вкладку Тесты.

    Снимок экрана: интерфейс Azure DevOps с выполнением конвейера и выделенной вкладкой

  6. Обратите внимание, что в сводке теста показано, что выполнены два теста. Один из них завершился с ошибкой, другой — успешно. У непройденного теста есть примечание: Toy Website. Не обслуживает страницы по HTTP.

    Снимок экрана: интерфейс Azure DevOps с результатами теста выполнения конвейера, выделен непройденный тест.

    Это означает, что веб-сайт был неправильно настроен и не отвечает требованиям вашей группы безопасности.

Обновление файла Bicep

Теперь, когда вы узнали, что определение Bicep не соответствует требованиям группы безопасности, вы исправите его.

  1. В Visual Studio Code откройте файл main.bicep в папке deploy.

  2. Найдите определение для приложения Службы приложений Azure и обновите его, включив свойство httpsOnly в область properties:

    resource appServiceApp 'Microsoft.Web/sites@2022-03-01' = {
      name: appServiceAppName
      location: location
      properties: {
        serverFarmId: appServicePlan.id
        httpsOnly: true
        siteConfig: {
          appSettings: [
            {
              name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
              value: applicationInsights.properties.InstrumentationKey
            }
            {
              name: 'APPLICATIONINSIGHTS_CONNECTION_STRING'
              value: applicationInsights.properties.ConnectionString
            }
          ]
        }
      }
    }
    
  3. Сохраните файл.

  4. Зафиксируйте и отправьте изменения в репозиторий Git, выполнив следующие команды в терминале Visual Studio Code:

    git add .
    git commit -m "Configure HTTPS on website"
    git push
    

Повторный запуск конвейера

  1. В браузере перейдите к конвейеру.

  2. Выберите последний запуск.

    Подождите, пока конвейер завершит этапы анализа кода, проверки и предварительного просмотра. Хотя Azure Pipelines автоматически обновляет страницу с учетом последнего состояния, рекомендуется периодически обновлять страницу.

  3. Выберите этап предварительной версии и снова просмотрите результаты "что если".

    Обратите внимание, что команда "что, если" обнаружила изменение в значении свойства httpsOnly:

    Resource and property changes are indicated with these symbols:
      + Create
      ~ Modify
      = Nochange
    
    The deployment will update the following scope:
    
    Scope: /subscriptions/f0750bbe-ea75-4ae5-b24d-a92ca601da2c/resourceGroups/ToyWebsiteTest
    
      ~ Microsoft.Web/sites/toy-website-nbfnedv766snk [2021-01-15]
        + properties.siteConfig.localMySqlEnabled:   false
        + properties.siteConfig.netFrameworkVersion: "v4.6"
        ~ properties.httpsOnly:                      false => true
    
      = Microsoft.Insights/components/toywebsite [2020-02-02]
      = Microsoft.Storage/storageAccounts/mystoragenbfnedv766snk [2021-04-01]
      = Microsoft.Web/serverfarms/toy-website [2021-01-15]
    
    Resource changes: 1 to modify, 3 no change.
    
  4. Вернитесь к выполнению конвейера.

  5. Нажмите кнопку "Рецензирование ", а затем нажмите кнопку "Утвердить".

    Дождитесь завершения выполнения конвейера.

  6. Обратите внимание, что весь конвейер завершается успешно, включая этап тестирования состояния. Это означает, что оба теста пройдены.

    Снимок экрана: интерфейс Azure DevOps с успешным выполнением конвейера.

Очистка ресурсов

Теперь, когда вы завершили упражнение, можно удалить ресурсы, чтобы не оплачивать их.

В окне терминала Visual Studio Code выполните следующую команду:

az group delete --resource-group ToyWebsiteTest --yes --no-wait

Группа ресурсов удалится в фоновом режиме.

Remove-AzResourceGroup -Name ToyWebsiteTest -Force