Commit 989f3b34 authored by rancher-max's avatar rancher-max Committed by Brad Davidson

Update terraform package and make running locally easier

parent 990ba0e8
# Terraform (TF) Tests
Terraform (TF) tests are an additional form of End-to-End (E2E) tests that cover multi-node K3s configuration and administration: install, update, teardown, etc. across a wide range of operating systems. Terraform tests are used as part of K3s quality assurance (QA) to bring up clusters with different configurations on demand, perform specific functionality tests, and keep them up and running to perform some exploratory tests in real-world scenarios.
## Framework
TF tests utilize [Ginkgo](https://onsi.github.io/ginkgo/) and [Gomega](https://onsi.github.io/gomega/) like the e2e tests. They rely on [Terraform](https://www.terraform.io/) to provide the underlying cluster configuration.
## Format
- All TF tests should be placed under `tests/terraform/<TEST_NAME>`.
- All TF test functions should be named: `Test_TF<TEST_NAME>`.
See the [create cluster test](../tests/terraform/createcluster_test.go) as an example.
## Running
Before running the tests, it's best to create a tfvars file in `./tests/terraform/modules/k3scluster/config/local.tfvars`. There is some information there to get you started, but the empty variables should be filled in appropriately per your AWS environment.
All TF tests can be run with:
```bash
go test -timeout=60m ./tests/terrfaorm/... -run TF
```
Tests can be run individually with:
```bash
go test -timeout=30m ./tests/terraform/createcluster.go ./tests/terraform/createcluster_test.go ./tests/terraform/testutils.go
# example with vars:
go test -timeout=30m -v ./tests/terraform/createcluster.go ./tests/terraform/createcluster_test.go ./tests/terraform/testutils.go -node_os=ubuntu -aws_ami=ami-02f3416038bdb17fb -cluster_type=etcd -resource_name=localrun1 -sshuser=ubuntu -sshkey="key-name" -destroy=false
```
In between tests, if the cluster is not destroyed, then make sure to delete the ./tests/terraform/terraform.tfstate file if you want to create a new cluster.
Additionally, to generate junit reporting for the tests, the Ginkgo CLI is used. Installation instructions can be found [here.](https://onsi.github.io/ginkgo/#getting-started)
To run the all TF tests and generate JUnit testing reports:
```
ginkgo --junit-report=result.xml ./tests/terraform/...
```
Note: The `go test` default timeout is 10 minutes, thus the `-timeout` flag should be used. The `ginkgo` default timeout is 1 hour, no timeout flag is needed.
# Debugging
The cluster and VMs can be retained after a test by passing `-destroy=false`.
To focus individual runs on specific test clauses, you can prefix with `F`. For example, in the [create cluster test](../tests/terraform/createcluster_test.go), you can upate the initial creation to be: `FIt("Starts up with no issues", func() {` in order to focus the run on only that clause.
\ No newline at end of file
package e2e package terraform
import ( import (
"flag" "flag"
...@@ -11,13 +11,18 @@ import ( ...@@ -11,13 +11,18 @@ import (
) )
var destroy = flag.Bool("destroy", false, "a bool") var destroy = flag.Bool("destroy", false, "a bool")
var nodeOs = flag.String("node_os", "centos8", "a string") var awsAmi = flag.String("aws_ami", "", "a valid ami string like ami-abcxyz123")
var nodeOs = flag.String("node_os", "ubuntu", "a string")
var externalDb = flag.String("external_db", "mysql", "a string") var externalDb = flag.String("external_db", "mysql", "a string")
var arch = flag.String("arch", "amd64", "a string") var arch = flag.String("arch", "amd64", "a string")
var clusterType = flag.String("cluster_type", "etcd", "a string") var clusterType = flag.String("cluster_type", "etcd", "a string")
var resourceName = flag.String("resource_name", "etcd", "a string") var resourceName = flag.String("resource_name", "etcd", "a string")
var sshuser = flag.String("sshuser", "ubuntu", "a string") var sshuser = flag.String("sshuser", "ubuntu", "a string")
var sshkey = flag.String("sshkey", "", "a string") var sshkey = flag.String("sshkey", "", "a string")
var access_key = flag.String("access_key", "", "local path to the private sshkey")
var tfVars = flag.String("tfvars", "./modules/k3scluster/config/local.tfvars", "custom .tfvars file")
var serverNodes = flag.Int("no_of_server_nodes", 2, "count of server nodes")
var workerNodes = flag.Int("no_of_worker_nodes", 1, "count of worker nodes")
var failed = false var failed = false
var ( var (
...@@ -26,43 +31,44 @@ var ( ...@@ -26,43 +31,44 @@ var (
workerIPs string workerIPs string
) )
func BuildCluster(nodeOs, clusterType, externalDb, resourceName string, t *testing.T, destroy bool, arch string) (string, string, string, error) { func BuildCluster(nodeOs, awsAmi string, clusterType, externalDb, resourceName string, t *testing.T, destroy bool, arch string) (string, error) {
tDir := "./modules/k3scluster" tDir := "./modules/k3scluster"
vDir := "/config/" + nodeOs + clusterType + ".tfvars"
if externalDb != "" {
vDir = "/config/" + nodeOs + externalDb + ".tfvars"
}
tfDir, err := filepath.Abs(tDir) tfDir, err := filepath.Abs(tDir)
if err != nil { if err != nil {
return "", "", "", err return "", err
} }
varDir, err := filepath.Abs(vDir) varDir, err := filepath.Abs(*tfVars)
if err != nil { if err != nil {
return "", "", "", err return "", err
} }
TerraformOptions := &terraform.Options{ TerraformOptions := &terraform.Options{
TerraformDir: tfDir, TerraformDir: tfDir,
VarFiles: []string{varDir}, VarFiles: []string{varDir},
Vars: map[string]interface{}{ Vars: map[string]interface{}{
"cluster_type": clusterType, "node_os": nodeOs,
"resource_name": resourceName, "aws_ami": awsAmi,
"external_db": externalDb, "cluster_type": clusterType,
"resource_name": resourceName,
"external_db": externalDb,
"aws_user": *sshuser,
"key_name": *sshkey,
"access_key": *access_key,
"no_of_server_nodes": *serverNodes,
"no_of_worker_nodes": *workerNodes,
}, },
} }
if destroy { if destroy {
fmt.Printf("Cluster is being deleted") fmt.Printf("Cluster is being deleted")
terraform.Destroy(t, TerraformOptions) terraform.Destroy(t, TerraformOptions)
return "", "", "", err return "cluster destroyed", err
} }
fmt.Printf("Creating Cluster") fmt.Printf("Creating Cluster")
terraform.InitAndApply(t, TerraformOptions) terraform.InitAndApply(t, TerraformOptions)
kubeconfig := terraform.Output(t, TerraformOptions, "kubeconfig") + "_kubeconfig" kubeConfigFile = "/tmp/" + terraform.Output(t, TerraformOptions, "kubeconfig") + "_kubeconfig"
masterIPs := terraform.Output(t, TerraformOptions, "master_ips") masterIPs = terraform.Output(t, TerraformOptions, "master_ips")
workerIPs := terraform.Output(t, TerraformOptions, "worker_ips") workerIPs = terraform.Output(t, TerraformOptions, "worker_ips")
kubeconfigFile := "/config/" + kubeconfig return "cluster created", err
return kubeconfigFile, masterIPs, workerIPs, err
} }
...@@ -78,8 +78,8 @@ pipeline { ...@@ -78,8 +78,8 @@ pipeline {
/usr/bin/docker cp "${WORKSPACE}/tests/terraform/$AWS_SSH_KEY_NAME" "${RESOURCE_NAME}_${BUILD_NUMBER}":/config /usr/bin/docker cp "${WORKSPACE}/tests/terraform/$AWS_SSH_KEY_NAME" "${RESOURCE_NAME}_${BUILD_NUMBER}":/config
/usr/bin/docker exec ${RESOURCE_NAME}_${BUILD_NUMBER} /usr/local/go/bin/go test -v tests/terraform/createcluster.go \ /usr/bin/docker exec ${RESOURCE_NAME}_${BUILD_NUMBER} /usr/local/go/bin/go test -v tests/terraform/createcluster.go \
tests/terraform/createcluster_test.go tests/terraform/testutils.go -v \ tests/terraform/createcluster_test.go tests/terraform/testutils.go \
-timeout=2h -node_os=${NODE_OS} \ -timeout=1h -node_os=${NODE_OS} \
-cluster_type=${CLUSTER_TYPE} -external_db=${EXTERNAL_DB} -resource_name=${RESOURCE_NAME} \ -cluster_type=${CLUSTER_TYPE} -external_db=${EXTERNAL_DB} -resource_name=${RESOURCE_NAME} \
-sshuser=${AWS_USER} -sshkey="/config/${AWS_SSH_KEY_NAME}" -destroy=false -arch=${ARCH} -sshuser=${AWS_USER} -sshkey="/config/${AWS_SSH_KEY_NAME}" -destroy=false -arch=${ARCH}
......
region = "us-east-2"
qa_space = ""
create_lb = false
external_db_version = "5.7"
instance_class = "db.t2.micro"
db_group_name = "mysql5.7"
engine_mode = "provisioned"
db_username = ""
db_password = ""
username = ""
password = ""
ec2_instance_class = "t3a.medium"
vpc_id = ""
subnets = ""
availability_zone = "us-east-2a"
sg_id = ""
no_of_server_nodes = 2
no_of_worker_nodes = 1
server_flags = "token: test"
worker_flags = "token: test"
k3s_version = "v1.23.8+k3s2"
install_mode = "INSTALL_K3S_VERSION"
environment = "local"
\ No newline at end of file
...@@ -90,9 +90,6 @@ resource "aws_instance" "master" { ...@@ -90,9 +90,6 @@ resource "aws_instance" "master" {
provisioner "local-exec" { provisioner "local-exec" {
command = "sed s/127.0.0.1/\"${var.create_lb ? aws_route53_record.aws_route53[0].fqdn : aws_instance.master.public_ip}\"/g /tmp/${var.resource_name}_config >/tmp/${var.resource_name}_kubeconfig" command = "sed s/127.0.0.1/\"${var.create_lb ? aws_route53_record.aws_route53[0].fqdn : aws_instance.master.public_ip}\"/g /tmp/${var.resource_name}_config >/tmp/${var.resource_name}_kubeconfig"
} }
provisioner "local-exec" {
command = "sed s/127.0.0.1/\"${var.create_lb ? aws_route53_record.aws_route53[0].fqdn : aws_instance.master.public_ip}\"/g /tmp/${var.resource_name}_config >/config/${var.resource_name}_kubeconfig"
}
} }
data "template_file" "test" { data "template_file" "test" {
......
/usr/local/bin/docker build -f Dockerfile.build -t k3s_create_cluster .
package e2e package terraform
import ( import (
"bytes" "bytes"
......
package e2e package terraform
import ( import (
"flag" "flag"
...@@ -12,7 +12,7 @@ import ( ...@@ -12,7 +12,7 @@ import (
var upgradeVersion = flag.String("upgrade_version", "", "a string") var upgradeVersion = flag.String("upgrade_version", "", "a string")
func Test_E2EClusterUpgradeValidation(t *testing.T) { func Test_TFClusterUpgradeValidation(t *testing.T) {
RegisterFailHandler(Fail) RegisterFailHandler(Fail)
flag.Parse() flag.Parse()
RunSpecs(t, "Upgrade Cluster Test Suite") RunSpecs(t, "Upgrade Cluster Test Suite")
...@@ -21,8 +21,9 @@ func Test_E2EClusterUpgradeValidation(t *testing.T) { ...@@ -21,8 +21,9 @@ func Test_E2EClusterUpgradeValidation(t *testing.T) {
var _ = Describe("Test:", func() { var _ = Describe("Test:", func() {
Context("Build Cluster:", func() { Context("Build Cluster:", func() {
It("Starts up with no issues", func() { It("Starts up with no issues", func() {
kubeConfigFile, masterIPs, workerIPs, err = BuildCluster(*nodeOs, *clusterType, *externalDb, *resourceName, &testing.T{}, *destroy, *arch) status, err := BuildCluster(*nodeOs, *awsAmi, *clusterType, *externalDb, *resourceName, &testing.T{}, *destroy, *arch)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(status).To(Equal("cluster created"))
defer GinkgoRecover() defer GinkgoRecover()
fmt.Println("\nCLUSTER CONFIG:\nOS", *nodeOs, "BACKEND", *clusterType, *externalDb) fmt.Println("\nCLUSTER CONFIG:\nOS", *nodeOs, "BACKEND", *clusterType, *externalDb)
fmt.Printf("\nIPs:\n") fmt.Printf("\nIPs:\n")
...@@ -503,13 +504,14 @@ var _ = BeforeEach(func() { ...@@ -503,13 +504,14 @@ var _ = BeforeEach(func() {
} }
}) })
var _ = AfterSuite(func() {
if failed { // var _ = AfterSuite(func() {
fmt.Println("FAILED!") // if failed {
} else { // fmt.Println("FAILED!")
kubeConfigFile, masterIPs, workerIPs, err = BuildCluster(*nodeOs, *clusterType, *externalDb, *resourceName, &testing.T{}, *destroy, *arch) // } else {
if err != nil { // kubeConfigFile, masterIPs, workerIPs, err = BuildCluster(*nodeOs, *awsAmi, *clusterType, *externalDb, *resourceName, &testing.T{}, *destroy, *arch)
fmt.Println("Error Destroying Cluster", err) // if err != nil {
} // fmt.Println("Error Destroying Cluster", err)
} // }
}) // }
// })
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment