Руководство по Тестирование модулей Terraform в Azure с помощью TerratestTutorial: Test Terraform modules in Azure using Terratest

Примечание

Пример кода из этой статьи не работает в версии 012 и последующих.The sample code in this article does not work with version 0.12 (and greater).

С помощью модулей Terraform можно создавать многократно используемые, составные и тестируемые компоненты.You can use Azure Terraform modules to create reusable, composable, and testable components. Модули Terraform внедряют инкапсуляцию, которая удобна при реализации инфраструктуры в качестве процессов кода.Terraform modules incorporate encapsulation that's useful in implementing infrastructure as code processes.

Очень важно реализовать контроль качества при создании модулей Terraform.It's important to implement quality assurance when you create Terraform modules. К сожалению, доступно мало документации, в которой объясняется, как создавать модульные тесты и тесты интеграции в модулях Terraform.Unfortunately, limited documentation is available to explain how to author unit tests and integration tests in Terraform modules. В этом руководстве описывается инфраструктура тестирования и представлены рекомендации, реализованные при создании модулей Azure Terraform.This tutorial introduces a testing infrastructure and best practices that we adopted when we built our Azure Terraform modules.

Мы рассмотрели все наиболее популярные инфраструктуры тестирования и решили использовать Terratest для тестирования модулей Terraform.We looked at all the most popular testing infrastructures and chose Terratest to use for testing our Terraform modules. Terratest реализуется в виде библиотеки Go.Terratest is implemented as a Go library. Terratest предоставляет набор вспомогательных функций и шаблонов для распространенных задач тестирования инфраструктуры, таких как создание HTTP-запросов и доступ к определенной виртуальной машине по протоколу SSH.Terratest provides a collection of helper functions and patterns for common infrastructure testing tasks, like making HTTP requests and using SSH to access a specific virtual machine. В следующем списке приведены некоторые из основных преимуществ использования Terratest:The following list describes some of the major advantages of using Terratest:

  • Он предоставляет удобные вспомогательные приложения для проверки инфраструктуры.It provides convenient helpers to check infrastructure. Эта функция полезна, если вы хотите проверить реальную инфраструктуру в реальной среде.This feature is useful when you want to verify your real infrastructure in the real environment.
  • Структура папок хорошо упорядочена.The folder structure is clearly organized. Тестовые случаи хорошо организованы и соответствуют стандартной структуре папок модуля Terraform.Your test cases are organized clearly and follow the standard Terraform module folder structure.
  • Все тестовые случаи написаны на языке Go.All test cases are written in Go. Большинство разработчиков, использующих Terraform, знакомы с разработкой на Go.Most developers who use Terraform are Go developers. Если это ваш случай, вам не нужно осваивать еще один язык для использования Terratest.If you're a Go developer, you don't have to learn another programming language to use Terratest. Кроме того, единственные зависимости, необходимые для выполнения тестовых случаев в Terratest, — это Go и Terraform.Also, the only dependencies that are required for you to run test cases in Terratest are Go and Terraform.
  • Эта инфраструктура обладает большими возможностями расширения.The infrastructure is highly extensible. Вы можете расширить дополнительные функции, включая связанные с Azure, на Terratest.You can extend additional functions on top of Terratest, including Azure-specific features.

Предварительные требованияPrerequisites

Эту практическую статью можно применять для любой платформы.This hands-on article is platform-independent. Используемые здесь примеры кода можно выполнять в Windows, Linux или MacOS.You can run the code examples that we use in this article on Windows, Linux, or MacOS.

Прежде чем начать, установите следующее программное обеспечение:Before you begin, install the following software:

  • Язык программирования Go. Тестовые случаи Terraform написаны на языке Go.Go programming language: Terraform test cases are written in Go.
  • dep. dep — это средство управления зависимостями для Go.dep: dep is a dependency management tool for Go.
  • Azure CLI. Azure CLI — это программа командной строки, которую можно использовать для управления ресурсами Azure.Azure CLI: The Azure CLI is a command-line tool you can use to manage Azure resources. (Terraform поддерживает проверку подлинности в Azure с помощью субъекта-службы или через Azure CLI.)(Terraform supports authenticating to Azure through a service principal or via the Azure CLI.)
  • mage. Мы используем исполняемый файл mage, чтобы показать, как упростить выполнение случаев Terratest.mage: We use the mage executable to show you how to simplify running Terratest cases.

Создание модуля статической веб-страницыCreate a static webpage module

В этом руководстве вы создаете модуль Terraform, который подготавливает к работе статическую веб-страницу, отправив один HTML-файл в большой двоичный объект службы хранилища Azure.In this tutorial, you create a Terraform module that provisions a static webpage by uploading a single HTML file to an Azure Storage blob. Этот модуль предоставляет пользователям по всему миру доступ к веб-странице через URL-адрес, который возвращает модуль.This module gives users from around the world access to the webpage through a URL that the module returns.

Примечание

Создайте все файлы, описанные в этом разделе, в вашем расположении GOPATH.Create all files that are described in this section under your GOPATH location.

Сначала создайте папку с именем staticwebpage в папке src GoPath.First, create a new folder named staticwebpage under your GoPath src folder. Общая структура папок для работы с этим руководством показана в следующем примере.The overall folder structure of this tutorial is shown in the following example. (Файлы, отмеченные звездочкой (*), являются основным приоритетом в этом разделе.)Files marked with an asterisk (*) are the primary focus in this section.

 📁 GoPath/src/staticwebpage
   ├ 📁 examples
   │   └ 📁 hello-world
   │       ├ 📄 index.html
   │       └ 📄 main.tf
   ├ 📁 test
   │   ├ 📁 fixtures
   │   │   └ 📁 storage-account-name
   │   │       ├ 📄 empty.html
   │   │       └ 📄 main.tf
   │   ├ 📄 hello_world_example_test.go
   │   └ 📄 storage_account_name_unit_test.go
   ├ 📄 main.tf      (*)
   ├ 📄 outputs.tf   (*)
   └ 📄 variables.tf (*)

Модуль статической веб-страницы принимает три вида входных данных.The static webpage module accepts three inputs. Входные данные объявлены в файле ./variables.tf:The inputs are declared in ./variables.tf:

variable "location" {
  description = "The Azure region in which to create all resources."
}

variable "website_name" {
  description = "The website name to use to create related resources in Azure."
}

variable "html_path" {
  description = "The file path of the static home page HTML in your local file system."
  default     = "index.html"
}

Как упоминалось ранее в этой статье, этот модуль также выводит URL-адрес, объявленный в файле ./outputs.tf:As we mentioned earlier in the article, this module also outputs a URL that's declared in ./outputs.tf:

output "homepage_url" {
  value = azurerm_storage_blob.homepage.url
}

Основная логика модуля подготавливает четыре ресурса:The main logic of the module provisions four resources:

  • Группа ресурсов. Имя группы ресурсов — входные данные website_name с добавлением -staging-rg.resource group: The name of the resource group is the website_name input appended by -staging-rg.
  • Учетная запись хранения. Имя учетной записи хранения — входные данные website_name с добавлением data001.storage account: The name of the storage account is the website_name input appended by data001. Для соблюдения ограничений имени учетной записи хранения модуль удаляет все специальные символы и использует буквы в нижнем регистре во всем имени учетной записи хранения.To adhere to the name limitations of the storage account, the module removes all special characters and uses lowercase letters in the entire storage account name.
  • Контейнер с фиксированным именем. Контейнеру присваивается имя wwwroot, и он создается в учетной записи хранения.fixed name container: The container is named wwwroot and is created in the storage account.
  • Один HTML-файл. HTML-файл считывается из входных данных html_path и отправляется в wwwroot/index.html.single HTML file: The HTML file is read from the html_path input and uploaded to wwwroot/index.html.

Логика модуля статической веб-страницы реализована в ./main.tf:The static webpage module logic is implemented in ./main.tf:

resource "azurerm_resource_group" "main" {
  name     = "${var.website_name}-staging-rg"
  location = var.location
}

resource "azurerm_storage_account" "main" {
  name                     = "${lower(replace(var.website_name, "/[[:^alnum:]]/", ""))}data001"
  resource_group_name      = azurerm_resource_group.main.name
  location                 = azurerm_resource_group.main.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_container" "main" {
  name                  = "wwwroot"
  resource_group_name   = azurerm_resource_group.main.name
  storage_account_name  = azurerm_storage_account.main.name
  container_access_type = "blob"
}

resource "azurerm_storage_blob" "homepage" {
  name                   = "index.html"
  resource_group_name    = azurerm_resource_group.main.name
  storage_account_name   = azurerm_storage_account.main.name
  storage_container_name = azurerm_storage_container.main.name
  source                 = var.html_path
  type                   = "block"
  content_type           = "text/html"
}

Модульный тестUnit test

Terratest предназначен для тестов интеграции.Terratest is designed for integration tests. Для этой цели Terratest подготавливает реальные ресурсы в реальной среде.For that purpose, Terratest provisions real resources in a real environment. Иногда задания тестов интеграции могут стать чрезвычайно большими, особенно в том случае, если необходимо подготовить к работе множество ресурсов.Sometimes, integration test jobs can become exceptionally large, especially when you have a large number of resources to provision. Хорошим примером является упомянутая в предыдущем разделе логика, которая преобразовывает имена учетных записей хранения.The logic that converts storage account names that we refer to in the preceding section is a good example.

Но нам не нужно подготавливать какие-либо ресурсы.But, we don't really need to provision any resources. Требуется только убедиться в правильности логики преобразования имен.We only want to make sure that the naming conversion logic is correct. Благодаря гибкости Terratest мы можем использовать модульные тесты.Thanks to the flexibility of Terratest, we can use unit tests. Это выполняемые локально тестовые случаи (хотя требуется доступ к Интернету).Unit tests are local running test cases (although internet access is required). Модульные тестовые случаи выполняют команды terraform init и terraform plan, чтобы проанализировать выходные данные terraform plan и найти значения атрибутов для сравнения.Unit test cases execute terraform init and terraform plan commands to parse the output of terraform plan and look for the attribute values to compare.

В оставшейся части этого раздела описывается, как мы используем Terratest для реализации модульного теста, чтобы убедиться в правильности логики, используемой для преобразования имен учетных записей хранения.The rest of this section describes how we use Terratest to implement a unit test to make sure that the logic used to convert storage account names is correct. Нас интересуют только файлы, отмеченные звездочкой (*).We are interested only in the files marked with an asterisk (*).

 📁 GoPath/src/staticwebpage
   ├ 📁 examples
   │   └ 📁 hello-world
   │       ├ 📄 index.html
   │       └ 📄 main.tf
   ├ 📁 test
   │   ├ 📁 fixtures
   │   │   └ 📁 storage-account-name
   │   │       ├ 📄 empty.html                (*)
   │   │       └ 📄 main.tf                   (*)
   │   ├ 📄 hello_world_example_test.go
   │   └ 📄 storage_account_name_unit_test.go (*)
   ├ 📄 main.tf
   ├ 📄 outputs.tf
   └ 📄 variables.tf

Сначала мы используем пустой HTML-файл с именем ./test/fixtures/storage-account-name/empty.html в качестве заполнителя.First, we use an empty HTML file named ./test/fixtures/storage-account-name/empty.html as a placeholder.

В файле ./test/fixtures/storage-account-name/main.tf приведена структура тестового случая.The file ./test/fixtures/storage-account-name/main.tf is the test case frame. Он принимает одни входные данные website_name, которые также являются входными данными модульных тестов.It accepts one input, website_name, which is also the input of the unit tests. Ниже приведена логика:The logic is shown here:

variable "website_name" {
  description = "The name of your static website."
}

module "staticwebpage" {
  source       = "../../../"
  location     = "West US"
  website_name = var.website_name
  html_path    = "empty.html"
}

Основной компонент — это реализация модульных тестов в ./test/storage_account_name_unit_test.goThe major component is the implementation of the unit tests in ./test/storage_account_name_unit_test.go.

Разработчик Go, вероятно, заметит, что модульный тест соответствует подписи классической функции проверки Go, принимая аргумент типа *testing.T.Go developers probably will notice that the unit test matches the signature of a classic Go test function by accepting an argument of type *testing.T.

В тексте модульного теста есть всего пять случаев, определенных в переменной testCases (key — входные данные, а value — ожидаемые выходные).In the body of the unit test, we have a total of five cases that are defined in variable testCases (key as input, and value as expected output). Для каждого модульного тестового случая сначала выполняется команда terraform init, целью которой является папка средства тестирования (./test/fixtures/storage-account-name/).For each unit test case, we first run terraform init and target the test fixture folder (./test/fixtures/storage-account-name/).

Далее команда terraform plan с определенными входными данными тестового случая (ознакомьтесь с определением website_name в tfOptions) сохраняет результат в файл ./test/fixtures/storage-account-name/terraform.tfplan (не указан в общей структуре папок).Next, a terraform plan command that uses specific test case input (take a look at the website_name definition in tfOptions) saves the result to ./test/fixtures/storage-account-name/terraform.tfplan (not listed in the overall folder structure).

Этот файл результатов анализируется и преобразовывается в структуру для считывания в коде с помощью официального средства синтаксического анализа плана Terraform.This result file is parsed to a code-readable structure by using the official Terraform plan parser.

Теперь мы ищем атрибуты, которые нас интересуют (в данном случае name из azurerm_storage_account), и сравниваем результаты с ожидаемыми выходными данными:Now, we look for the attributes we're interested in (in this case, the name of the azurerm_storage_account) and compare the results with the expected output:

package test

import (
    "os"
    "path"
    "testing"

    "github.com/gruntwork-io/terratest/modules/terraform"
    terraformCore "github.com/hashicorp/terraform/terraform"
)

func TestUT_StorageAccountName(t *testing.T) {
    t.Parallel()

    // Test cases for storage account name conversion logic
    testCases := map[string]string{
        "TestWebsiteName": "testwebsitenamedata001",
        "ALLCAPS":         "allcapsdata001",
        "S_p-e(c)i.a_l":   "specialdata001",
        "A1phaNum321":     "a1phanum321data001",
        "E5e-y7h_ng":      "e5ey7hngdata001",
    }

    for input, expected := range testCases {
        // Specify the test case folder and "-var" options
        tfOptions := &terraform.Options{
            TerraformDir: "./fixtures/storage-account-name",
            Vars: map[string]interface{}{
                "website_name": input,
            },
        }

        // Terraform init and plan only
        tfPlanOutput := "terraform.tfplan"
        terraform.Init(t, tfOptions)
        terraform.RunTerraformCommand(t, tfOptions, terraform.FormatArgs(tfOptions, "plan", "-out="+tfPlanOutput)...)

        // Read and parse the plan output
        f, err := os.Open(path.Join(tfOptions.TerraformDir, tfPlanOutput))
        if err != nil {
            t.Fatal(err)
        }
        defer f.Close()
        plan, err := terraformCore.ReadPlan(f)
        if err != nil {
            t.Fatal(err)
        }

        // Validate the test result
        for _, mod := range plan.Diff.Modules {
            if len(mod.Path) == 2 && mod.Path[0] == "root" && mod.Path[1] == "staticwebpage" {
                actual := mod.Resources["azurerm_storage_account.main"].Attributes["name"].New
                if actual != expected {
                    t.Fatalf("Expect %v, but found %v", expected, actual)
                }
            }
        }
    }
}

Для выполнения модульных тестов сделайте следующее в командной строке.To run the unit tests, complete the following steps on the command line:

$ cd [Your GoPath]/src/staticwebpage
GoPath/src/staticwebpage$ dep init    # Run only once for this folder
GoPath/src/staticwebpage$ dep ensure  # Required to run if you imported new packages in test cases
GoPath/src/staticwebpage$ cd test
GoPath/src/staticwebpage/test$ go fmt
GoPath/src/staticwebpage/test$ az login    # Required when no service principal environment variables are present
GoPath/src/staticwebpage/test$ go test -run TestUT_StorageAccountName

Традиционный результат теста Go возвращается в течение одной минуты.The traditional Go test result returns in about a minute.

Тест интеграцииIntegration test

В отличие от модульных тестов тесты интеграции должны подготавливать ресурсы к реальной среде с точки зрения комплексной перспективы.In contrast to unit tests, integration tests must provision resources to a real environment for an end-to-end perspective. Terratest очень хорошо справляется с задачами такого рода.Terratest does a good job with this kind of task.

Рекомендации для модулей Terraform включают установку папки examples.Best practices for Terraform modules include installing the examples folder. Папка examples содержит некоторые полноценные примеры.The examples folder contains some end-to-end samples. Чтобы избежать работы с реальными данными, почему бы не протестировать эти примеры как тесты интеграции?To avoid working with real data, why not test those samples as integration tests? В этом разделе мы сосредоточимся на трех файлах, помеченных звездочкой (*), в следующей структуре папок:In this section, we focus on the three files that are marked with an asterisk (*) in the following folder structure:

 📁 GoPath/src/staticwebpage
   ├ 📁 examples
   │   └ 📁 hello-world
   │       ├ 📄 index.html              (*)
   │       └ 📄 main.tf                 (*)
   ├ 📁 test
   │   ├ 📁 fixtures
   │   │   └ 📁 storage-account-name
   │   │       ├ 📄 empty.html
   │   │       └ 📄 main.tf
   │   ├ 📄 hello_world_example_test.go (*)
   │   └ 📄 storage_account_name_unit_test.go
   ├ 📄 main.tf
   ├ 📄 outputs.tf
   └ 📄 variables.tf

Давайте начнем с примеров.Let's start with the samples. В папке ./examples/ создается пример папки с именем hello-world/.A new sample folder named hello-world/ is created in the ./examples/ folder. Здесь представлена простая страница HTML для отправки: ./examples/hello-world/index.html:Here, we provide a simple HTML page to be uploaded: ./examples/hello-world/index.html.

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Hello World</title>
</head>
<body>
    <h1>Hi, Terraform Module</h1>
    <p>This is a sample web page to demonstrate Terratest.</p>
</body>
</html>

Пример Terraform ./examples/hello-world/main.tf подобен примеру, показанному в модульном тесте.The Terraform sample ./examples/hello-world/main.tf is similar to the one shown in the unit test. Есть одно существенное различие: пример также выводит URL-адрес отправленного HTML-файла в качестве веб-страницы с именем homepage.There's one significant difference: the sample also prints out the URL of the uploaded HTML as a webpage named homepage.

variable "website_name" {
  description = "The name of your static website."
  default     = "Hello-World"
}

module "staticwebpage" {
  source       = "../../"
  location     = "West US"
  website_name = var.website_name
}

output "homepage" {
  value = module.staticwebpage.homepage_url
}

Мы используем Terratest и классические функции проверки Go еще раз в файле теста интеграции ./test/hello_world_example_test.go.We use Terratest and classic Go test functions again in the integration test file ./test/hello_world_example_test.go.

В отличие от модульных тестов тесты интеграции создают фактические ресурсы в Azure.Unlike unit tests, integration tests create actual resources in Azure. Вот почему необходимо быть осторожными, чтобы избежать конфликтов имен.That's why you need to be careful to avoid naming conflicts. (Обратите особое внимание на некоторые глобально уникальные имена, такие как имена учетных записей хранения.) Таким образом, первым шагом логики тестирования является создание случайного websiteName с помощью функции UniqueId(), предоставляемой Terratest.(Pay special attention to some globally unique names like storage account names.) Therefore, the first step of the testing logic is to generate a randomized websiteName by using the UniqueId() function provided by Terratest. Эта функция создает случайное имя, которое содержит буквы в нижнем и верхнем регистре или цифры.This function generates a random name that has lowercase letters, uppercase letters, or numbers. tfOptions выполняет все команды Terraform, нацеленные на папку ./examples/hello-world/.tfOptions makes all Terraform commands that target the ./examples/hello-world/ folder. Она также позволяет убедиться, что для параметра website_name задано случайное websiteName.It also makes sure that website_name is set to the randomized websiteName.

Затем одна за другой выполняются команды terraform init, terraform apply, и terraform output.Then, terraform init, terraform apply, and terraform output are executed, one by one. Мы используем другую вспомогательную функцию HttpGetWithCustomValidation(), предоставляемую Terratest,We use another helper function, HttpGetWithCustomValidation(), which is provided by Terratest. чтобы убедиться в том, что HTML передается в URL-адрес homepage выходных данных, возвращаемый в terraform output.We use the helper function to make sure that HTML is uploaded to the output homepage URL that's returned by terraform output. Мы сравниваем код состояния HTTP GET с 200 и выполняем поиск некоторых ключевых слов в содержимом HTML.We compare the HTTP GET status code with 200 and look for some keywords in the HTML content. Наконец, обеспечивается выполнение terraform destroy с помощью функции defer Go.Finally, terraform destroy is "promised" to be executed by leveraging the defer feature of Go.

package test

import (
    "fmt"
    "strings"
    "testing"

    "github.com/gruntwork-io/terratest/modules/http-helper"
    "github.com/gruntwork-io/terratest/modules/random"
    "github.com/gruntwork-io/terratest/modules/terraform"
)

func TestIT_HelloWorldExample(t *testing.T) {
    t.Parallel()

    // Generate a random website name to prevent a naming conflict
    uniqueID := random.UniqueId()
    websiteName := fmt.Sprintf("Hello-World-%s", uniqueID)

    // Specify the test case folder and "-var" options
    tfOptions := &terraform.Options{
        TerraformDir: "../examples/hello-world",
        Vars: map[string]interface{}{
            "website_name": websiteName,
        },
    }

    // Terraform init, apply, output, and destroy
    defer terraform.Destroy(t, tfOptions)
    terraform.InitAndApply(t, tfOptions)
    homepage := terraform.Output(t, tfOptions, "homepage")

    // Validate the provisioned webpage
    http_helper.HttpGetWithCustomValidation(t, homepage, func(status int, content string) bool {
        return status == 200 &&
            strings.Contains(content, "Hi, Terraform Module") &&
            strings.Contains(content, "This is a sample web page to demonstrate Terratest.")
    })
}

Для выполнения тестов интеграции сделайте следующее в командной строке:To run the integration tests, complete the following steps on the command line:

$ cd [Your GoPath]/src/staticwebpage
GoPath/src/staticwebpage$ dep init    # Run only once for this folder
GoPath/src/staticwebpage$ dep ensure  # Required to run if you imported new packages in test cases
GoPath/src/staticwebpage$ cd test
GoPath/src/staticwebpage/test$ go fmt
GoPath/src/staticwebpage/test$ az login    # Required when no service principal environment variables are present
GoPath/src/staticwebpage/test$ go test -run TestIT_HelloWorldExample

Традиционный результат теста Go возвращается в течение двух минут.The traditional Go test result returns in about two minutes. Вы также можете запустить и модульные тесты, и тесты интеграции, выполнив эти команды:You could also run both unit tests and integration tests by executing these commands:

GoPath/src/staticwebpage/test$ go fmt
GoPath/src/staticwebpage/test$ go test

Тесты интеграции занимают гораздо больше времени, чем модульные (две минуты для одного случая интеграции в сравнении с одной минутой для пяти модульных случаев).Integration tests take much longer than unit tests (two minutes for one integration case compared to one minute for five unit cases). Но вам решать, что использовать в сценарии: модульные тесты или тесты интеграции.But it's your decision whether to use unit tests or integration tests in a scenario. Как правило, мы предпочитаем применять модульные тесты для сложной логики с использованием функций Terraform HCL,Typically, we prefer to use unit tests for complex logic by using Terraform HCL functions. а тесты интеграции — с точки зрения комплексной перспективы пользователя.We usually use integration tests for the end-to-end perspective of a user.

Упрощение обработки случаев Terratest с помощью mageUse mage to simplify running Terratest cases

Для запуска тестовых случаев в Azure Cloud Shell требуется выполнить разные команды в разных каталогах.Running test cases in Azure Cloud Shell requires executing different commands in various directories. Чтобы сделать этот процесс более эффективным, мы представляем систему сборки в нашем проекте.To make this process more efficient, we introduce the build system in our project. В этом разделе для выполнения задания мы используем систему сборки Go — mage.In this section, we use a Go build system, mage, for the job.

Единственное, что требуется mage, это файл magefile.go в корневом каталоге вашего проекта (помечен символом (+) в следующем примере).The only thing required by mage is magefile.go in your project's root directory (marked with (+) in the following example):

 📁 GoPath/src/staticwebpage
   ├ 📁 examples
   │   └ 📁 hello-world
   │       ├ 📄 index.html
   │       └ 📄 main.tf
   ├ 📁 test
   │   ├ 📁 fixtures
   │   │   └ 📁 storage-account-name
   │   │       ├ 📄 empty.html
   │   │       └ 📄 main.tf
   │   ├ 📄 hello_world_example_test.go
   │   └ 📄 storage_account_name_unit_test.go
   ├ 📄 magefile.go (+)
   ├ 📄 main.tf
   ├ 📄 outputs.tf
   └ 📄 variables.tf

Ниже приведен пример ./magefile.go.Here's an example of ./magefile.go. В этом скрипте сборки на языке Go мы реализовали пять шагов сборки:In this build script, written in Go, we implement five build steps:

  • Clean: На этом шаге удаляются все сгенерированные и временные файлы, созданные во время выполнения тестов.Clean: The step removes all generated and temporary files that are generated during test executions.
  • Format: На этом шаге выполняется terraform fmt и go fmt для форматирования базы кода.Format: The step runs terraform fmt and go fmt to format your code base.
  • Unit: На этом шаге выполняются все модульные тесты (с использованием соглашения об именовании функции TestUT_*) в папке ./test/.Unit: The step runs all unit tests (by using the function name convention TestUT_*) under the ./test/ folder.
  • Integration: Этот шаг аналогичен Unit, но в отличие от модульных тестов на этом этапе выполняются тесты интеграции (TestIT_*).Integration: The step is similar to Unit, but instead of unit tests, it executes integration tests (TestIT_*).
  • Full: На этом шаге последовательно выполняются Clean, Format, Unit и Integration.Full: The step runs Clean, Format, Unit, and Integration in sequence.
// +build mage

// Build a script to format and run tests of a Terraform module project
package main

import (
    "fmt"
    "os"
    "path/filepath"

    "github.com/magefile/mage/mg"
    "github.com/magefile/mage/sh"
)

// The default target when the command executes `mage` in Cloud Shell
var Default = Full

// A build step that runs Clean, Format, Unit and Integration in sequence
func Full() {
    mg.Deps(Unit)
    mg.Deps(Integration)
}

// A build step that runs unit tests
func Unit() error {
    mg.Deps(Clean)
    mg.Deps(Format)
    fmt.Println("Running unit tests...")
    return sh.RunV("go", "test", "./test/", "-run", "TestUT_", "-v")
}

// A build step that runs integration tests
func Integration() error {
    mg.Deps(Clean)
    mg.Deps(Format)
    fmt.Println("Running integration tests...")
    return sh.RunV("go", "test", "./test/", "-run", "TestIT_", "-v")
}

// A build step that formats both Terraform code and Go code
func Format() error {
    fmt.Println("Formatting...")
    if err := sh.RunV("terraform", "fmt", "."); err != nil {
        return err
    }
    return sh.RunV("go", "fmt", "./test/")
}

// A build step that removes temporary build and test files
func Clean() error {
    fmt.Println("Cleaning...")
    return filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if info.IsDir() && info.Name() == "vendor" {
            return filepath.SkipDir
        }
        if info.IsDir() && info.Name() == ".terraform" {
            os.RemoveAll(path)
            fmt.Printf("Removed \"%v\"\n", path)
            return filepath.SkipDir
        }
        if !info.IsDir() && (info.Name() == "terraform.tfstate" ||
            info.Name() == "terraform.tfplan" ||
            info.Name() == "terraform.tfstate.backup") {
            os.Remove(path)
            fmt.Printf("Removed \"%v\"\n", path)
        }
        return nil
    })
}

Для выполнения полного набора тестов можно использовать следующие команды.You can use the following commands to execute a full test suite. Код аналогичен шагам, которые выполнялись в предыдущем разделе.The code is similar to the running steps we used in an earlier section.

$ cd [Your GoPath]/src/staticwebpage
GoPath/src/staticwebpage$ dep init    # Run only once for this folder
GoPath/src/staticwebpage$ dep ensure  # Required to run if you imported new packages in magefile or test cases
GoPath/src/staticwebpage$ go fmt      # Only required when you change the magefile
GoPath/src/staticwebpage$ az login    # Required when no service principal environment variables are present
GoPath/src/staticwebpage$ mage

Последнюю командную строку можно заменить дополнительными действиями mage.You can replace the last command line with additional mage steps. Например, вы можете использовать mage unit или mage clean.For example, you can use mage unit or mage clean. Хорошая идея — внедрить команды dep и az login в magefile.It's a good idea to embed dep commands and az login in the magefile. Мы не показываем код здесь.We don't show the code here.

С mage вы можете также предоставить общий доступ к действиям, используя пакетную систему Go.With mage, you could also share the steps by using the Go package system. В этом случае файлы magefile во всех модулях можно упростить, просто ссылаясь на общую реализацию и объявляя зависимости (mg.Deps()).In that case, you can simplify magefiles across all your modules by referencing only a common implementation and declaring dependencies (mg.Deps()).

Необязательно. Установка переменных среды субъекта-службы для выполнения тестов приемкиOptional: Set service principal environment variables to run acceptance tests

Вместо выполнения команды az login перед тестом проверку подлинности Azure можно выполнить, задав переменные среды субъекта-службы.Instead of executing az login before tests, you can complete Azure authentication by setting the service principal environment variables. Terraform выводит список имен переменных среды.Terraform publishes a list of environment variable names. (Требуются только первые четыре переменных среды.) Terraform также выводит подробные инструкции о том, как получить значение этих переменных среды.(Only the first four of these environment variables are required.) Terraform also publishes detailed instructions that explain how to obtain the value of these environment variables.

Дополнительная информацияNext steps