51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 643|回复: 0
打印 上一主题 下一主题

[资料] 浅谈IaC测试工具之Terratest

[复制链接]
  • TA的每日心情
    无聊
    3 天前
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2023-6-8 10:43:52 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    一 背景
      针对IaC编写的tf/Dockerfile/Kubernetes的 yaml /helm charts文件,传统测试通过执行命令,例如针对hcl语言的init/plan/apply,最后在进行destroy清理,针对单个module测试手动还可以接受,但对与多数模块及需要持续性的测试,需要利用其他工具解决,terratest利用golang的testing模块配合gruntwork-io的terratest对IaC代码进行测试,根据业务特征将其集成在CI/CD流水线中,确保基础代码的可测试性。
      二 简介
      Terratest 是一个开源的 Go 语言库,可更轻松地为基础设施代码编写自动化测试。它为通用基础设施的测试任务提供了各种帮助函数和模式,其中包括:
      ·测试 Terraform 代码
      · 测试打包器模板
      · 测试 Docker 镜像
      · 通过 SSH 在服务器上执行命令
      · 使用 AWS API
      · 发送 HTTP 请求
      · 运行 shell 命令
      · 使用Kubernetes API
      · 测试helm
      三 测试
      3.1 前置条件
      Terratest使用Go测试框架。要使用Terratest,您需要安装:
      Go(要求版本>=1.18)
      3.2 设置项目
      开始使用Terratest最简单的方法是从这个repo中复制一个示例及其相应的测试。这个快速入门部分使用了一个Terraform示例,但是请查看示例部分,了解您可以测试的其他类型的基础设施代码(例如,Packer、Kubernetes等)。
      1. 创建一个examples 和 test 目录
      2. clone代码basic terraform example到examples
      3. clone代码 basic terraform example test 到test目录
      4. cd 到test目录执行一下初始化
       cd test
       go mod init terratest
       go mod tidy


      完成后目录结构:
      ├── src
      │   ├── main.tf
      │   ├── outputs.tf
      │   ├── varfile.tfvars
      │   └── variables.tf
      └── test
          ├── go.mod
          └── test.go


      接着进行:
       cd test
       go test -v -timeout 30m


      3.3 对tf文件进行测试
      · tf文件
      该代码仅简单输出一个"Hello, World!"字符串。
      terraform {
        # This module is now only being tested with Terraform 0.13.x. However, to make upgrading easier, we are setting
        # 0.12.26 as the minimum version, as that version added support for required_providers with source URLs, making it
        # forwards compatible with 0.13.x code.
        required_version = ">= 0.12.26"
      }
      # The simplest possible Terraform module: it just outputs "Hello, World!"
      output "hello_world" {
        value = "Hello, World!"
      }


      传统的利用terraform进行测试需要init/plan/apply,之后进行destroy清理。
      利用terratest可省去以上繁琐命令,集成为代码中进行统一测试
      · terraform_hello_world_example_test.go
      package test
      import (
      "testing"
      "github.com/gruntwork-io/terratest/modules/terraform"
      "github.com/stretchr/testify/assert"
      )
      func TestTerraformHelloWorldExample(t *testing.T) {
      // Construct the terraform options with default retryable errors to handle the most common
      // retryable errors in terraform testing.
      terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
      // Set the path to the Terraform code that will be tested.
      TerraformDir: "../examples/terraform-hello-world-example",
      })
      // Clean up resources with "terraform destroy" at the end of the test.
      defer terraform.Destroy(t, terraformOptions)
      // Run "terraform init" and "terraform apply". Fail the test if there are any errors.
      terraform.InitAndApply(t, terraformOptions)
      // Run `terraform output` to get the values of output variables and check they have the expected values.
      output := terraform.Output(t, terraformOptions, "hello_world")
      assert.Equal(t, "Hello, World!", output)
      }


      以上代码完成手工执行所有步骤,包括运行terraform init,terraform apply,使用terraform output读取输出变量,检查它的值是我们所期望的,以及运行terraform destroy(使用defer在测试结束时运行它,无论测试成功还是失败)。如果此代码放入名为terraform_hello_world_example_test.go的文件中,您可以通过执行go test来运行它,您将看到如下所示的输出(为了可读性而截断):
      $ go test -v -timeout 30m
      === RUN   TestTerraformHelloWorldExample
      Running command terraform with args [init]
      Initializing provider plugins...
      [...]
      Terraform has been successfully initialized!
      [...]
      Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
      Outputs:
      hello_world = "Hello, World!"
      [...]
      Running command terraform with args [destroy -force -input=false]
      [...]
      Destroy complete! Resources: 2 destroyed.
      --- PASS: TestTerraformHelloWorldExample (149.36s)


      3.4 测试Dockerfile
      · Dockerfile
      # Build a simple Docker image that contains a text file with the contents "Hello, World!"
      FROM ubuntu:18.04
      RUN echo 'Hello, World!' > /test.txt


      传统测试Dockerfile使用docker build,之后docker run查看是否正常生成了内容为'Hello, World!'的test.txt文件,利用terratest可写成代码。
      · docker_hello_world_example_test.go
      package test
      import (
      "testing"
      "github.com/gruntwork-io/terratest/modules/docker"
      "github.com/stretchr/testify/assert"
      )
      func TestDockerHelloWorldExample(t *testing.T) {
      // Configure the tag to use on the Docker image.
      tag := "gruntwork/docker-hello-world-example"
      buildOptions := &docker.BuildOptions{
      Tags: []string{tag},
      }
      // Build the Docker image.
      docker.Build(t, "../examples/docker-hello-world-example", buildOptions)
      // Run the Docker image, read the text file from it, and make sure it contains the expected output.
      opts := &docker.RunOptions{Command: []string{"cat", "/test.txt"}}
      output := docker.Run(t, tag, opts)
      assert.Equal(t, "Hello, World!", output)
      }


      3.5 测试Kubernetes资源清单
      · hello-world-deployment.yml
      ---
      # Deploy the training/webapp Docker Container: https://hub.docker.com/r/training/webapp/
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: hello-world-deployment
      spec:
        selector:
          matchLabels:
            app: hello-world
        replicas: 1
        template:
          metadata:
            labels:
              app: hello-world
          spec:
            containers:
              # The container runs a Python webapp on port 5000 that responds with "Hello, World!"
              - name: hello-world
                image: training/webapp:latest
                ports:
                  - containerPort: 5000
      ---
      # Expose the Python webapp on port 5000 via a Kubernetes LoadBalancer.
      kind: Service
      apiVersion: v1
      metadata:
        name: hello-world-service
      spec:
        selector:
          app: hello-world
        ports:
          - protocol: TCP
            targetPort: 5000
            port: 5000
        type: LoadBalancer


      · kubernetes_hello_world_example_test.go
      //go:build kubeall || kubernetes
      // +build kubeall kubernetes
      // NOTE: See the notes in the other Kubernetes example tests for why this build tag is included.
      package test
      import (
      "fmt"
      "testing"
      "time"
      http_helper "github.com/gruntwork-io/terratest/modules/http-helper"
      "github.com/gruntwork-io/terratest/modules/k8s"
      )
      func TestKubernetesHelloWorldExample(t *testing.T) {
      t.Parallel()
      // Path to the Kubernetes resource config we will test.
      kubeResourcePath := "../examples/kubernetes-hello-world-example/hello-world-deployment.yml"
      // Setup the kubectl config and context.
      options := k8s.NewKubectlOptions("", "", "default")
      // At the end of the test, run "kubectl delete" to clean up any resources that were created.
      defer k8s.KubectlDelete(t, options, kubeResourcePath)
      // Run `kubectl apply` to deploy. Fail the test if there are any errors.
      k8s.KubectlApply(t, options, kubeResourcePath)
      // Verify the service is available and get the URL for it.
      k8s.WaitUntilServiceAvailable(t, options, "hello-world-service", 10, 1*time.Second)
      service := k8s.GetService(t, options, "hello-world-service")
      url := fmt.Sprintf("http://%s", k8s.GetServiceEndpoint(t, options, service, 5000))
      // Make an HTTP request to the URL and make sure it returns a 200 OK with the body "Hello, World".
      http_helper.HttpGetWithRetry(t, url, nil, 200, "Hello world!", 30, 3*time.Second)
      }


      四 其他
      为了使这种测试更容易,Terratest为常见的基础设施测试任务提供了各种助手函数和模式,例如测试Terraform代码、测试Packer模板、测试Docker映像、通过SSH在服务器上执行命令、发出HTTP请求、使用AWS APIs等等。
      注意:在执行go test -timeout 30m,这是因为Go将默认测试时间设置为10分钟,如果你的测试时间超过10分钟,可能导致程序panic,因此可通过外置参数进行自定义时长。
      五 总结
      terratest利用go语言的testing模块非常方便的为基础代码编写单元测试,确保每次代码改动都获得预先的结果,配合在CI/CD流程中可以帮助更安全更高效的对基础代码进行管控。通过terratest,确保每次对这个Terraform代码进行更改时,测试代码都可以运行,并确保您的最终云基础资源按预期工作。

    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-24 20:43 , Processed in 0.064287 second(s), 23 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表