Skip to content

Commit ec20c1e

Browse files
committed
fixup! feat: Add preflight checks framework
Deterministically order results, and fix status reporting
1 parent b377301 commit ec20c1e

File tree

2 files changed

+361
-90
lines changed

2 files changed

+361
-90
lines changed

pkg/webhook/preflight/preflight.go

Lines changed: 44 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -81,73 +81,72 @@ func (h *WebhookHandler) Handle(ctx context.Context, req admission.Request) admi
8181
},
8282
}
8383

84-
// Collect all checks in parallel.
85-
checkerWG := &sync.WaitGroup{}
86-
resultCh := make(chan CheckResult)
87-
for _, checker := range h.checkers {
88-
checkerWG.Add(1)
89-
90-
go func(ctx context.Context, checker Checker, resultCh chan CheckResult) {
91-
// Initialize the checker.
92-
checks := checker.Init(ctx, h.client, cluster)
93-
94-
// Run its checks in parallel.
95-
checksWG := &sync.WaitGroup{}
96-
for _, check := range checks {
97-
checksWG.Add(1)
98-
go func(ctx context.Context, check Check, resultCh chan CheckResult) {
99-
result := check(ctx)
100-
resultCh <- result
101-
checksWG.Done()
102-
}(ctx, check, resultCh)
103-
}
104-
checksWG.Wait()
105-
106-
checkerWG.Done()
107-
}(ctx, checker, resultCh)
108-
}
84+
results := run(ctx, h.client, cluster, h.checkers)
10985

110-
// Close the channel when all checkers are done.
111-
go func(wg *sync.WaitGroup, resultCh chan CheckResult) {
112-
wg.Wait()
113-
close(resultCh)
114-
}(checkerWG, resultCh)
115-
116-
// Collect all check results from the channel.
86+
// Summarize the results.
11787
internalError := false
118-
for result := range resultCh {
88+
for _, result := range results {
11989
if result.Error {
12090
internalError = true
12191
}
122-
12392
if !result.Allowed {
12493
resp.Allowed = false
12594
}
126-
12795
for _, cause := range result.Causes {
12896
resp.Result.Details.Causes = append(resp.Result.Details.Causes, metav1.StatusCause{
12997
Type: metav1.CauseType(fmt.Sprintf("FailedPreflight%s", result.Name)),
13098
Message: cause.Message,
13199
Field: cause.Field,
132100
})
133101
}
134-
135102
resp.Warnings = append(resp.Warnings, result.Warnings...)
136103
}
137104

138-
if len(resp.Result.Details.Causes) == 0 {
139-
return resp
140-
}
141-
142-
// Because we have some causes, we construct the response message and code.
143-
resp.Result.Message = "preflight checks failed"
144-
resp.Result.Code = http.StatusForbidden
145-
resp.Result.Reason = metav1.StatusReasonForbidden
146-
if internalError {
105+
switch {
106+
case internalError:
147107
// Internal errors take precedence over check failures.
108+
resp.Result.Message = "preflight checks failed due to an internal error"
148109
resp.Result.Code = http.StatusInternalServerError
149110
resp.Result.Reason = metav1.StatusReasonInternalError
111+
case !resp.Allowed:
112+
// Because the response is not allowed, preflights must have failed.
113+
resp.Result.Message = "preflight checks failed"
114+
resp.Result.Code = http.StatusUnprocessableEntity
115+
resp.Result.Reason = metav1.StatusReasonInvalid
150116
}
151117

152118
return resp
153119
}
120+
121+
func run(ctx context.Context, client ctrlclient.Client, cluster *clusterv1.Cluster, checkers []Checker) []CheckResult {
122+
resultsOrderedByCheckerAndCheck := make([][]CheckResult, len(checkers))
123+
124+
checkersWG := sync.WaitGroup{}
125+
for i, checker := range checkers {
126+
checkersWG.Add(1)
127+
go func(ctx context.Context, client ctrlclient.Client, cluster *clusterv1.Cluster, checker Checker, i int) {
128+
checks := checker.Init(ctx, client, cluster)
129+
resultsOrderedByCheck := make([]CheckResult, len(checks))
130+
131+
checksWG := sync.WaitGroup{}
132+
for j, check := range checks {
133+
checksWG.Add(1)
134+
go func(ctx context.Context, check Check, j int) {
135+
result := check(ctx)
136+
resultsOrderedByCheck[j] = result
137+
checksWG.Done()
138+
}(ctx, check, j)
139+
}
140+
checksWG.Wait()
141+
resultsOrderedByCheckerAndCheck[i] = resultsOrderedByCheck
142+
checkersWG.Done()
143+
}(ctx, client, cluster, checker, i)
144+
}
145+
checkersWG.Wait()
146+
147+
results := []CheckResult{}
148+
for _, resultsByCheck := range resultsOrderedByCheckerAndCheck {
149+
results = append(results, resultsByCheck...)
150+
}
151+
return results
152+
}

0 commit comments

Comments
 (0)