-
Notifications
You must be signed in to change notification settings - Fork 444
/
requirements.go
187 lines (156 loc) · 5.23 KB
/
requirements.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
174
175
176
177
178
179
180
181
182
183
184
185
186
187
package testutils
import (
"fmt"
"os"
"runtime"
"github.com/hashicorp/go-multierror"
"github.com/onsi/ginkgo/v2"
"k8s.io/apimachinery/pkg/util/sets"
)
// ValidateRequirementsAndNotifyGinkgo validates that the provided Requirements are met, and if they are not, uses
// the InvalidTestReqsEnvVar to determine how to proceed:
// Options are:
// - `run`: Ignore any invalid requirements and execute the tests
// - `skip`: Notify Ginkgo that the current spec was skipped
// - `fail`: Notify Ginkgo that the current spec has failed [DEFAULT]
func ValidateRequirementsAndNotifyGinkgo(requirements ...Requirement) {
err := ValidateRequirements(requirements)
if err == nil {
return
}
message := fmt.Sprintf("Test requirements not met: %v \n\n Consider using %s=skip to skip these tests", err, InvalidTestReqsEnvVar)
switch os.Getenv(InvalidTestReqsEnvVar) {
case "run":
// ignore the error from validating requirements and let the tests proceed
return
case "skip":
ginkgo.Skip(message)
case "fail":
fallthrough
default:
ginkgo.Fail(message)
}
}
// ValidateRequirements returns an error if any of the Requirements are not met
func ValidateRequirements(requirements []Requirement) error {
// default
requiredConfiguration := &RequiredConfiguration{
supportedOS: sets.NewString(),
supportedArch: sets.NewString(),
reasons: map[string]string{},
}
// apply requirements
for _, requirement := range requirements {
requirement(requiredConfiguration)
}
// perform validation
return requiredConfiguration.Validate()
}
type RequiredConfiguration struct {
supportedOS sets.String
supportedArch sets.String
// Set of env variables which must be defined
definedEnvVar []string
// Set of env variables which must have a truthy value
// Examples: "1", "t", "T", "true", "TRUE", "True"
truthyEnvVar []string
// User defined reasons for why particular environmental conditions are required
reasons map[string]string
}
// Validate returns an error is the RequiredConfiguration is not met
func (r RequiredConfiguration) Validate() error {
var errs *multierror.Error
errs = multierror.Append(
errs,
r.validateOS(),
r.validateArch(),
r.validateDefinedEnv(),
r.validateTruthyEnv())
// If there are no errors, return
if errs.ErrorOrNil() == nil {
return nil
}
// If there are reasons defined, include them in the error message
if len(r.reasons) > 0 {
errs = multierror.Append(
errs,
fmt.Errorf("user defined reasons: %+v", r.reasons))
}
return errs.ErrorOrNil()
}
func (r RequiredConfiguration) validateOS() error {
if r.supportedOS.Len() == 0 {
// An empty set is considered to support all
return nil
}
if r.supportedOS.Has(runtime.GOOS) {
return nil
}
return fmt.Errorf("runtime os (%s), is not in supported set (%v)", runtime.GOOS, r.supportedOS.UnsortedList())
}
func (r RequiredConfiguration) validateArch() error {
if r.supportedArch.Len() == 0 {
// An empty set is considered to support all
return nil
}
if r.supportedArch.Has(runtime.GOARCH) {
return nil
}
return fmt.Errorf("runtime arch (%s), is not in supported set (%v)", runtime.GOARCH, r.supportedArch.UnsortedList())
}
func (r RequiredConfiguration) validateDefinedEnv() error {
for _, env := range r.definedEnvVar {
if _, found := os.LookupEnv(env); !found {
return fmt.Errorf("env (%s) is not defined", env)
}
}
return nil
}
func (r RequiredConfiguration) validateTruthyEnv() error {
for _, env := range r.truthyEnvVar {
if !IsEnvTruthy(env) {
return fmt.Errorf("env (%s) needs to be truthy", env)
}
}
return nil
}
// Requirement represents a required property for tests.
type Requirement func(configuration *RequiredConfiguration)
// LinuxOnly returns a Requirement that expects tests to only run on Linux
func LinuxOnly(reason string) Requirement {
return func(configuration *RequiredConfiguration) {
configuration.supportedOS = sets.NewString("linux")
configuration.reasons["linux"] = reason
}
}
// DefinedEnv returns a Requirement that expects tests to have the injected environment variable defined
func DefinedEnv(env string) Requirement {
return func(configuration *RequiredConfiguration) {
configuration.definedEnvVar = append(configuration.definedEnvVar, env)
}
}
// TruthyEnv returns a Requirement that expects tests to have the injected environment variable set to a truthy value
func TruthyEnv(env string) Requirement {
return func(configuration *RequiredConfiguration) {
configuration.truthyEnvVar = append(configuration.truthyEnvVar, env)
}
}
// Consul returns a Requirement that expects tests to require a Consul instance
func Consul() Requirement {
return func(configuration *RequiredConfiguration) {
TruthyEnv(RunConsulTests)(configuration)
}
}
// Vault returns a Requirement that expects tests to require a Vault instance
func Vault() Requirement {
return func(configuration *RequiredConfiguration) {
TruthyEnv(RunVaultTests)(configuration)
}
}
// AwsCredentials returns a Requirement that expects tests to require Aws credentials
func AwsCredentials() Requirement {
return func(configuration *RequiredConfiguration) {
DefinedEnv("AWS_SHARED_CREDENTIALS_FILE")(configuration)
configuration.reasons["aws"] = "AWS_SHARED_CREDENTIALS_FILE defines the file location where AWS credentials are stored"
}
}