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