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 }