Тестирование инфраструктуры как код с помощью Pulumi. Часть 2

Всем привет. Сегодня делимся с вами заключительной частью статьи «Тестирование инфраструктуры как код с помощью Pulumi», перевод которой подготовлен специально для студентов курса «DevOps практики и инструменты».

Тестирование инфраструктуры как код с помощью Pulumi. Часть 2

Тестирование развертывания

Рассмотренный стиль тестирования — это мощный подход, он позволяет нам проводить тестирование белого ящика для проверки внутренностей работы нашего инфраструктурного кода. Однако он несколько ограничивает то, что мы можем проверить. Тесты выполняются на основе in-memory плана развертывания, созданного Pulumi перед непосредственным развертыванием и поэтому само развертывание не протестировать. Для таких случаев в Pulumi есть фреймворк интеграционных тестов. И эти два подхода отлично работают вместе!

Фреймворк интеграционного тестирования Pulumi написан на Go, и именно с его помощью мы тестируем большую часть нашего внутреннего кода. Если рассмотренный ранее подход модульного тестирования был больше похож на тестирование белого ящика, то интеграционное тестирование — это черный ящик. (Есть также варианты тщательного внутреннего тестирования.) Этот фреймворк был создан для того, чтобы взять полную Pulumi-программу и выполнить для нее различные операции жизненного цикла, такие как развертывание нового стека с нуля, его обновление с вариациями и удаление, возможно несколько раз. Мы запускаем их регулярно (например, ночью) и в качестве стресс-тестов.

(Мы работаем над тем, чтобы аналогичные возможности интеграционного тестирования были в родном SDK языков. Вы можете использовать фреймворк интеграционного тестирования Go независимо от языка, на котором написана ваша Pulumi-программа).

Запустив программу с помощью этого фреймворка вы можете проверить следующее:

  • Код вашего проекта синтаксически правильный и работает без ошибок.
  • Настройки конфигурации стека и секретов работают и интерпретируются правильно.
  • Ваш проект может быть успешно развернут в выбранном вами облачном провайдере.
  • Ваш проект может быть успешно обновлен с начального состояния до N других состояний.
  • Ваш проект может быть успешно уничтожен и удален из вашего облачного провайдера.

Как мы скоро увидим, этот фреймворк можно использовать также для выполнения runtime-валидации.

Простой интеграционный тест

Чтобы увидеть это в действии, мы посмотрим на репозиторий pulumi/examples, так как наша команда и сообщество Pulumi, использует его для тестирования собственных пул реквестов, коммитов и ночных сборок.

Ниже приведен упрощенный тест нашего примера, который делает провиженинг S3 bucket и некоторых других объектов:

example_test.go:

package test
 
import (
    "os"
    "path"
    "testing"
 
    "github.com/pulumi/pulumi/pkg/testing/integration"
)
 
func TestExamples(t *testing.T) {
    awsRegion := os.Getenv("AWS_REGION")
    if awsRegion == "" {
        awsRegion = "us-west-1"
    }
    cwd, _ := os.Getwd()
    integration.ProgramTest(t, &integration.ProgramTestOptions{
        Quick:       true,
        SkipRefresh: true,
        Dir:         path.Join(cwd, "..", "..", "aws-js-s3-folder"),
        Config: map[string]string{
            "aws:region": awsRegion,
        },
    })
}

Этот тест проходит через базовый жизненный цикл создания, изменения и уничтожения стека для папки aws-js-s3-folder. Он займет около минуты, чтобы сообщить о пройденном тесте:

$ go test .
PASS
ok      ... 43.993s

Есть много параметров для настройки поведения этих тестов. Полный список опций см. в структуре ProgramTestOptions. Например, вы можете настроить Jaeger endpoint для трассировки (Tracing), указать, что ожидаете падения теста при негативном тестировании (ExpectFailure), применить серию “правок” к программе для последовательного перехода состояний (EditDirs) и многое другое. Давайте посмотрим, как использовать их для проверки развертывания приложения.

Проверка свойств ресурсов

Интеграция, о которой говорили выше, гарантирует, что наша программа «работает» — она не падает. Но что, если мы хотим, проверить свойства полученного стека? Например, что определенные виды ресурсов были (или не были) подготовлены и что они имеют определенные атрибуты.

Параметр ExtraRuntimeValidation для ProgramTestOptions позволяет нам посмотреть на состояние, зафиксированное Pulumi после развертывания (post-deployment state), чтобы мы могли сделать дополнительные проверки. Сюда входит полный снимок состояния результирующего стека, включая конфигурацию, экспортируемые выходные значения, все ресурсы и значения их свойств, а также все зависимости между ресурсами.

Чтобы увидеть базовый пример этого, давайте проверим, что наша программа создает один S3 Bucket:

  integration.ProgramTest(t, &integration.ProgramTestOptions{
        // as before...
        ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
            var foundBuckets int
            for _, res := range stack.Deployment.Resources {
                if res.Type == "aws:s3/bucket:Bucket" {
                    foundBuckets++
                }
            }
            assert.Equal(t, 1, foundBuckets, "Expected to find a single AWS S3 Bucket")
        },
    })

Теперь, когда мы запустим go test, он не только пройдет через батарею тестов жизненного цикла, но также, после успешного развертывания стека, выполнит дополнительную проверку результирующего состояния.

Runtime-тесты

До сих пор все тесты были исключительно о поведении при развертывании и о модели ресурсов Pulumi. Что делать, если вы хотите проверить, что ваша подготовленная инфраструктура действительно работает? Например, что виртуальная машина работает, S3 bucket содержит то, что мы ожидаем, и так далее.

Вы, возможно, уже догадались, как это сделать: опция ExtraRuntimeValidation для ProgramTestOptions — это отличная возможность для этого. На этом этапе вы запускаете произвольный тест Go с доступом к полному состоянию ресурсов вашей программы. Это состояние включает в себя такую информацию, как IP-адреса виртуальных машин, URL-адреса и все, что необходимо для реального взаимодействия с полученными облачными приложениями и инфраструктурой.

Например, наша тестовая программа экспортирует свойство webEndpoint bucket’а под названием websiteUrl, которое представляет собой полный URL-адрес, по которому мы можем получить настроенный index document. Хотя мы могли бы покопаться в файле состояния, чтобы найти bucket и прочитать это свойство напрямую, но во многих случаях наши стеки экспортируют полезные свойства, такие как это, которые нам удобно использовать для проверки:

integration.ProgramTest(t, &integration.ProgramTestOptions{
            // as before ...
        ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
            url := "http://" + stack.Outputs["websiteUrl"].(string)
            resp, err := http.Get(url)
            if !assert.NoError(t, err) {
                return
            }
            if !assert.Equal(t, 200, resp.StatusCode) {
                return
            }
            defer resp.Body.Close()
            body, err := ioutil.ReadAll(resp.Body)
            if !assert.NoError(t, err) {
                return
            }
            assert.Contains(t, string(body), "Hello, Pulumi!")
        },
    })

Как и наши предыдущие runtime-проверки, эта проверка будет выполняться сразу после поднятия стека, и все это в ответ на простой вызов go test. И это только вершина айсберга — доступны все тестовые возможности Go, которые вы можете написать в коде.

Непрерывная интеграция инфраструктуры

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

  • В каждом открытом пул реквесте для теста перед слиянием.
  • В ответ на каждый коммит, чтобы перепроверить, что слияние было выполнено правильно.
  • Периодически, например, ночью или еженедельно для дополнительного тестирования.
  • В рамках тестирования производительности или стресс-тестирования, которые обычно выполняются в течение длительного периода времени и запускают тесты параллельно и/или развертывают одну и ту же программу несколько раз.

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

В Pulumi есть поддержка распространенных CI-систем. Вот некоторые из них:

Для получения более подробной информации обратитесь к документации по Continuous Delivery.

Эфемерные окружения

Очень мощная возможность, которая открывается — это возможность разворачивать эфемерные окружения исключительно для целей приемочного тестирования. Концепция проектов и стеков Pulumi разработана таким образом, чтобы легко развертывать и сносить полностью изолированные и независимые окружения, все в несколько простых команд CLI или с помощью фреймворка интеграционного тестирования.

Если вы используете GitHub, то Pulumi предлагает GitHub App, которое поможет вам подключить приемочное тестирование к пул реквестам внутри вашего CI-пайплайна. Просто установите приложение в репозиторий GitHub, а Pulumi в ваш CI и в пул реквесты будет добавляться информация о превью инфраструктуры, обновлениях и результатах тестирования:

Тестирование инфраструктуры как код с помощью Pulumi. Часть 2

При использовании Pulumi для ваших основных приемочных тестов, у вас появятся новые возможности автоматизации, которые улучшат производительность команды и придадут уверенность в качестве изменений.

Итог

В этой статье мы увидели, что, при использовании языков программирования общего назначения, нам становятся доступны многие методы разработки программного обеспечения, которые были полезны при разработке наших приложений. Они включают в себя модульное тестирование, интеграционное тестирование, а также их взаимодействие для проведения обширного runtime-тестирования. Тесты легко запускать по требованию или в вашей CI-системе.

Pulumi — программное обеспечение с открытым исходным кодом, оно бесплатно для использования и работает с вашими любимыми языками программирования и облаками — попробуйте его сегодня!

Первая часть

Источник: habr.com