github.com/darmach/terratest@v0.34.8-0.20210517103231-80931f95e3ff/test/terraform_ssh_password_example_test.go (about) 1 package test 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 "time" 8 9 "github.com/gruntwork-io/terratest/modules/aws" 10 "github.com/gruntwork-io/terratest/modules/random" 11 "github.com/gruntwork-io/terratest/modules/retry" 12 "github.com/gruntwork-io/terratest/modules/ssh" 13 "github.com/gruntwork-io/terratest/modules/terraform" 14 test_structure "github.com/gruntwork-io/terratest/modules/test-structure" 15 ) 16 17 // An example of how to test the Terraform module in examples/terraform-ssh-password-example using Terratest. The test 18 // also shows an example of how to break a test down into "stages" so you can skip stages by setting environment 19 // variables (e.g., skip stage "teardown" by setting the environment variable "SKIP_teardown=true"), which speeds up 20 // iteration when running this test over and over again locally. 21 func TestTerraformSshPasswordExample(t *testing.T) { 22 t.Parallel() 23 24 exampleFolder := test_structure.CopyTerraformFolderToTemp(t, "../", "examples/terraform-ssh-password-example") 25 26 // At the end of the test, run `terraform destroy` to clean up any resources that were created. 27 defer test_structure.RunTestStage(t, "teardown", func() { 28 terraformOptions := test_structure.LoadTerraformOptions(t, exampleFolder) 29 terraform.Destroy(t, terraformOptions) 30 }) 31 32 // Deploy the example. 33 test_structure.RunTestStage(t, "setup", func() { 34 terraformOptions := configureTerraformSshPasswordOptions(t, exampleFolder) 35 36 // Save the options so later test stages can use them. 37 test_structure.SaveTerraformOptions(t, exampleFolder, terraformOptions) 38 39 // This will run `terraform init` and `terraform apply` and fail the test if there are any errors. 40 terraform.InitAndApply(t, terraformOptions) 41 }) 42 43 // Make sure we can SSH to the public instance directly from the public internet. 44 test_structure.RunTestStage(t, "validate", func() { 45 terraformOptions := test_structure.LoadTerraformOptions(t, exampleFolder) 46 47 testSSHPasswordToPublicHost(t, terraformOptions) 48 }) 49 } 50 51 func configureTerraformSshPasswordOptions(t *testing.T, exampleFolder string) *terraform.Options { 52 // A unique ID we can use to namespace resources so we don't clash with anything already in the AWS account or 53 // tests running in parallel. 54 uniqueID := random.UniqueId() 55 56 // Give this EC2 instance and other resources in the Terraform code a name with a unique ID so it doesn't clash 57 // with anything else in the AWS account. 58 instanceName := fmt.Sprintf("terratest-ssh-password-example-%s", uniqueID) 59 60 // Pick a random AWS region to test in. This helps ensure your code works in all regions. 61 awsRegion := aws.GetRandomStableRegion(t, nil, nil) 62 63 // Some AWS regions are missing certain instance types, so pick an available type based on the region we picked 64 instanceType := aws.GetRecommendedInstanceType(t, awsRegion, []string{"t2.micro", "t3.micro"}) 65 66 // Create a random password that we can use for SSH access. 67 password := random.UniqueId() 68 69 // Construct the terraform options with default retryable errors to handle the most common retryable errors in 70 // terraform testing. 71 terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{ 72 // The path to where our Terraform code is located. 73 TerraformDir: exampleFolder, 74 75 // Variables to pass to our Terraform code using -var options. 76 Vars: map[string]interface{}{ 77 "aws_region": awsRegion, 78 "instance_name": instanceName, 79 "instance_type": instanceType, 80 "terratest_password": password, 81 }, 82 }) 83 84 return terraformOptions 85 } 86 87 func testSSHPasswordToPublicHost(t *testing.T, terraformOptions *terraform.Options) { 88 // Run `terraform output` to get the value of an output variable. 89 publicInstanceIP := terraform.Output(t, terraformOptions, "public_instance_ip") 90 91 // We're going to try to SSH to the instance IP, using the username and password that will be set up (by 92 // Terraform's user_data script) in the instance. 93 publicHost := ssh.Host{ 94 Hostname: publicInstanceIP, 95 Password: terraformOptions.Vars["terratest_password"].(string), 96 SshUserName: "terratest", 97 } 98 99 // It can take a minute or so for the instance to boot up, so retry a few times. 100 maxRetries := 30 101 timeBetweenRetries := 5 * time.Second 102 description := fmt.Sprintf("SSH to public host %s", publicInstanceIP) 103 104 // Run a simple echo command on the server. 105 expectedText := "Hello, World" 106 command := fmt.Sprintf("echo -n '%s'", expectedText) 107 108 // Verify that we can SSH to the instance and run commands. 109 retry.DoWithRetry(t, description, maxRetries, timeBetweenRetries, func() (string, error) { 110 actualText, err := ssh.CheckSshCommandE(t, publicHost, command) 111 112 if err != nil { 113 return "", err 114 } 115 116 if strings.TrimSpace(actualText) != expectedText { 117 return "", fmt.Errorf("Expected SSH command to return '%s' but got '%s'", expectedText, actualText) 118 } 119 120 return "", nil 121 }) 122 123 // Run a command on the server that results in an error. 124 expectedText = "Hello, World" 125 command = fmt.Sprintf("echo -n '%s' && exit 1", expectedText) 126 description = fmt.Sprintf("SSH to public host %s with error command", publicInstanceIP) 127 128 // Verify that we can SSH to the instance, run the command which forces an error, and see the output. 129 retry.DoWithRetry(t, description, maxRetries, timeBetweenRetries, func() (string, error) { 130 actualText, err := ssh.CheckSshCommandE(t, publicHost, command) 131 132 if err == nil { 133 return "", fmt.Errorf("Expected SSH command to return an error but got none") 134 } 135 136 if strings.TrimSpace(actualText) != expectedText { 137 return "", fmt.Errorf("Expected SSH command to return '%s' but got '%s'", expectedText, actualText) 138 } 139 140 return "", nil 141 }) 142 }