github.com/terraform-modules-krish/terratest@v0.29.0/modules/terraform/count.go (about)

     1  package terraform
     2  
     3  import (
     4  	"errors"
     5  	"regexp"
     6  	"strconv"
     7  
     8  	"github.com/terraform-modules-krish/terratest/modules/testing"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  // ResourceCount represents counts of resources affected by terraform apply/plan/destroy command.
    13  type ResourceCount struct {
    14  	Add     int
    15  	Change  int
    16  	Destroy int
    17  }
    18  
    19  // Regular expressions for terraform commands stdout pattern matching.
    20  const (
    21  	applyRegexp             = `Apply complete! Resources: (\d+) added, (\d+) changed, (\d+) destroyed\.`
    22  	destroyRegexp           = `Destroy complete! Resources: (\d+) destroyed\.`
    23  	planWithChangesRegexp   = `(\033\[1m)?Plan:(\033\[0m)? (\d+) to add, (\d+) to change, (\d+) to destroy\.`
    24  	planWithNoChangesRegexp = `No changes\. Infrastructure is up-to-date\.`
    25  )
    26  
    27  const getResourceCountErrMessage = "Can't parse Terraform output"
    28  
    29  // GetResourceCount parses stdout/stderr of apply/plan/destroy commands and returns number of affected resources.
    30  // This will fail the test if given stdout/stderr isn't a valid output of apply/plan/destroy.
    31  func GetResourceCount(t testing.TestingT, cmdout string) *ResourceCount {
    32  	cnt, err := GetResourceCountE(t, cmdout)
    33  	require.NoError(t, err)
    34  	return cnt
    35  }
    36  
    37  // GetResourceCountE parses stdout/stderr of apply/plan/destroy commands and returns number of affected resources.
    38  func GetResourceCountE(t testing.TestingT, cmdout string) (*ResourceCount, error) {
    39  	cnt := ResourceCount{}
    40  
    41  	terraformCommandPatterns := []struct {
    42  		regexpStr       string
    43  		addPosition     int
    44  		changePosition  int
    45  		destroyPosition int
    46  	}{
    47  		{applyRegexp, 1, 2, 3},
    48  		{destroyRegexp, -1, -1, 1},
    49  		{planWithChangesRegexp, 3, 4, 5},
    50  		{planWithNoChangesRegexp, -1, -1, -1},
    51  	}
    52  
    53  	for _, tc := range terraformCommandPatterns {
    54  		pattern, err := regexp.Compile(tc.regexpStr)
    55  		if err != nil {
    56  			return nil, err
    57  		}
    58  
    59  		matches := pattern.FindStringSubmatch(cmdout)
    60  		if matches != nil {
    61  			if tc.addPosition != -1 {
    62  				cnt.Add, err = strconv.Atoi(matches[tc.addPosition])
    63  				if err != nil {
    64  					return nil, err
    65  				}
    66  			}
    67  
    68  			if tc.changePosition != -1 {
    69  				cnt.Change, err = strconv.Atoi(matches[tc.changePosition])
    70  				if err != nil {
    71  					return nil, err
    72  				}
    73  			}
    74  
    75  			if tc.destroyPosition != -1 {
    76  				cnt.Destroy, err = strconv.Atoi(matches[tc.destroyPosition])
    77  				if err != nil {
    78  					return nil, err
    79  				}
    80  			}
    81  
    82  			return &cnt, nil
    83  		}
    84  	}
    85  
    86  	return nil, errors.New(getResourceCountErrMessage)
    87  }