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  }