github.com/mponton/terratest@v0.44.0/modules/version-checker/version_checker.go (about)

     1  package version_checker
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  
     7  	"github.com/hashicorp/go-version"
     8  	"github.com/mponton/terratest/modules/shell"
     9  	"github.com/mponton/terratest/modules/testing"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  // VersionCheckerBinary is an enum for supported version checking.
    14  type VersionCheckerBinary int
    15  
    16  // List of binaries supported for version checking.
    17  const (
    18  	Docker VersionCheckerBinary = iota
    19  	Terraform
    20  	Packer
    21  )
    22  
    23  const (
    24  	// versionRegexMatcher is a regex used to extract version string from shell command output.
    25  	versionRegexMatcher = `\d+(\.\d+)+`
    26  	// defaultVersionArg is a default arg to pass in to get version output from shell command.
    27  	defaultVersionArg = "--version"
    28  )
    29  
    30  type CheckVersionParams struct {
    31  	// BinaryPath is a path to the binary you want to check the version for.
    32  	BinaryPath string
    33  	// Binary is the name of the binary you want to check the version for.
    34  	Binary VersionCheckerBinary
    35  	// VersionConstraint is a string literal containing one or more conditions, which are separated by commas.
    36  	// More information here:https://www.terraform.io/language/expressions/version-constraints
    37  	VersionConstraint string
    38  	// WorkingDir is a directory you want to run the shell command.
    39  	WorkingDir string
    40  }
    41  
    42  // CheckVersionE checks whether the given Binary version is greater than or equal
    43  // to the given expected version.
    44  func CheckVersionE(
    45  	t testing.TestingT,
    46  	params CheckVersionParams) error {
    47  
    48  	if err := validateParams(params); err != nil {
    49  		return err
    50  	}
    51  
    52  	binaryVersion, err := getVersionWithShellCommand(t, params)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	return checkVersionConstraint(binaryVersion, params.VersionConstraint)
    58  }
    59  
    60  // CheckVersion checks whether the given Binary version is greater than or equal to the
    61  // given expected version and fails if it's not.
    62  func CheckVersion(
    63  	t testing.TestingT,
    64  	params CheckVersionParams) {
    65  	require.NoError(t, CheckVersionE(t, params))
    66  }
    67  
    68  // Validate whether the given params contains valid data to check version.
    69  func validateParams(params CheckVersionParams) error {
    70  	// Check for empty parameters
    71  	if params.WorkingDir == "" {
    72  		return fmt.Errorf("set WorkingDir in params")
    73  	} else if params.VersionConstraint == "" {
    74  		return fmt.Errorf("set VersionConstraint in params")
    75  	}
    76  
    77  	// Check the format of the version constraint if present.
    78  	if _, err := version.NewConstraint(params.VersionConstraint); params.VersionConstraint != "" && err != nil {
    79  		return fmt.Errorf(
    80  			"invalid version constraint format found {%s}", params.VersionConstraint)
    81  	}
    82  
    83  	return nil
    84  }
    85  
    86  // getVersionWithShellCommand get version by running a shell command.
    87  func getVersionWithShellCommand(t testing.TestingT, params CheckVersionParams) (string, error) {
    88  	var versionArg = defaultVersionArg
    89  	binary, err := getBinary(params)
    90  	if err != nil {
    91  		return "", err
    92  	}
    93  
    94  	// Run a shell command to get the version string.
    95  	output, err := shell.RunCommandAndGetOutputE(t, shell.Command{
    96  		Command:    binary,
    97  		Args:       []string{versionArg},
    98  		WorkingDir: params.WorkingDir,
    99  		Env:        map[string]string{},
   100  	})
   101  	if err != nil {
   102  		return "", fmt.Errorf("failed to run shell command for Binary {%s} "+
   103  			"w/ version args {%s}: %w", binary, versionArg, err)
   104  	}
   105  
   106  	versionStr, err := extractVersionFromShellCommandOutput(output)
   107  	if err != nil {
   108  		return "", fmt.Errorf("failed to extract version from shell "+
   109  			"command output {%s}: %w", output, err)
   110  	}
   111  
   112  	return versionStr, nil
   113  }
   114  
   115  // getBinary retrieves the binary to use from the given params.
   116  func getBinary(params CheckVersionParams) (string, error) {
   117  	// Use BinaryPath if it is set, otherwise use the binary enum.
   118  	if params.BinaryPath != "" {
   119  		return params.BinaryPath, nil
   120  	}
   121  
   122  	switch params.Binary {
   123  	case Docker:
   124  		return "docker", nil
   125  	case Packer:
   126  		return "packer", nil
   127  	case Terraform:
   128  		return "terraform", nil
   129  	default:
   130  		return "", fmt.Errorf("unsupported Binary for checking versions {%d}", params.Binary)
   131  	}
   132  }
   133  
   134  // extractVersionFromShellCommandOutput extracts version with regex string matching
   135  // from the given shell command output string.
   136  func extractVersionFromShellCommandOutput(output string) (string, error) {
   137  	regexMatcher := regexp.MustCompile(versionRegexMatcher)
   138  	versionStr := regexMatcher.FindString(output)
   139  	if versionStr == "" {
   140  		return "", fmt.Errorf("failed to find version using regex matcher")
   141  	}
   142  
   143  	return versionStr, nil
   144  }
   145  
   146  // checkVersionConstraint checks whether the given version pass the version constraint.
   147  //
   148  // It returns Error for ill-formatted version string and VersionMismatchErr for
   149  // minimum version check failure.
   150  //
   151  //	checkVersionConstraint(t, "1.2.31",  ">= 1.2.0, < 2.0.0") - no error
   152  //	checkVersionConstraint(t, "1.0.31",  ">= 1.2.0, < 2.0.0") - error
   153  func checkVersionConstraint(actualVersionStr string, versionConstraintStr string) error {
   154  	actualVersion, err := version.NewVersion(actualVersionStr)
   155  	if err != nil {
   156  		return fmt.Errorf("invalid version format found for actualVersionStr: %s", actualVersionStr)
   157  	}
   158  
   159  	versionConstraint, err := version.NewConstraint(versionConstraintStr)
   160  	if err != nil {
   161  		return fmt.Errorf("invalid version format found for versionConstraint: %s", versionConstraintStr)
   162  	}
   163  
   164  	if !versionConstraint.Check(actualVersion) {
   165  		return &VersionMismatchErr{
   166  			errorMessage: fmt.Sprintf("actual version {%s} failed "+
   167  				"the version constraint {%s}", actualVersionStr, versionConstraint),
   168  		}
   169  
   170  	}
   171  
   172  	return nil
   173  }