Всем привет. Сегодня делимся с вами заключительной частью статьи
Тестирование развертывания
Рассмотренный стиль тестирования — это мощный подход, он позволяет нам проводить тестирование белого ящика для проверки внутренностей работы нашего инфраструктурного кода. Однако он несколько ограничивает то, что мы можем проверить. Тесты выполняются на основе in-memory плана развертывания, созданного Pulumi перед непосредственным развертыванием и поэтому само развертывание не протестировать. Для таких случаев в Pulumi есть фреймворк интеграционных тестов. И эти два подхода отлично работают вместе!
Фреймворк интеграционного тестирования Pulumi написан на Go, и именно с его помощью мы тестируем большую часть нашего внутреннего кода. Если рассмотренный ранее подход модульного тестирования был больше похож на тестирование белого ящика, то интеграционное тестирование — это черный ящик. (Есть также варианты тщательного внутреннего тестирования.) Этот фреймворк был создан для того, чтобы взять полную Pulumi-программу и выполнить для нее различные операции жизненного цикла, такие как развертывание нового стека с нуля, его обновление с вариациями и удаление, возможно несколько раз. Мы запускаем их регулярно (например, ночью) и в качестве стресс-тестов.
(Мы
Запустив программу с помощью этого фреймворка вы можете проверить следующее:
- Код вашего проекта синтаксически правильный и работает без ошибок.
- Настройки конфигурации стека и секретов работают и интерпретируются правильно.
- Ваш проект может быть успешно развернут в выбранном вами облачном провайдере.
- Ваш проект может быть успешно обновлен с начального состояния до N других состояний.
- Ваш проект может быть успешно уничтожен и удален из вашего облачного провайдера.
Как мы скоро увидим, этот фреймворк можно использовать также для выполнения runtime-валидации.
Простой интеграционный тест
Чтобы увидеть это в действии, мы посмотрим на репозиторий pulumi/examples
, так как наша команда и сообщество Pulumi, использует его для тестирования собственных пул реквестов, коммитов и ночных сборок.
Ниже приведен упрощенный тест нашего
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-систем. Вот некоторые из них:
Для получения более подробной информации обратитесь к документации по
Эфемерные окружения
Очень мощная возможность, которая открывается — это возможность разворачивать эфемерные окружения исключительно для целей приемочного тестирования. Концепция
Если вы используете GitHub, то Pulumi предлагает
При использовании Pulumi для ваших основных приемочных тестов, у вас появятся новые возможности автоматизации, которые улучшат производительность команды и придадут уверенность в качестве изменений.
Итог
В этой статье мы увидели, что, при использовании языков программирования общего назначения, нам становятся доступны многие методы разработки программного обеспечения, которые были полезны при разработке наших приложений. Они включают в себя модульное тестирование, интеграционное тестирование, а также их взаимодействие для проведения обширного runtime-тестирования. Тесты легко запускать по требованию или в вашей CI-системе.
Pulumi — программное обеспечение с открытым исходным кодом, оно бесплатно для использования и работает с вашими любимыми языками программирования и облаками —
→
Источник: habr.com