Merge pull request #109120 from kevindelgado/ss-validation-metrics

Track field validation in metrics
This commit is contained in:
Kubernetes Prow Robot 2022-03-29 20:37:20 -07:00 committed by GitHub
commit c7fc0f9125
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 0 deletions

View File

@ -122,6 +122,19 @@ var (
},
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
)
fieldValidationRequestLatencies = compbasemetrics.NewHistogramVec(
&compbasemetrics.HistogramOpts{
Name: "field_validation_request_duration_seconds",
Help: "Response latency distribution in seconds for each field validation value and whether field validation is enabled or not",
// This metric is supplementary to the requestLatencies metric.
// It measures request durations for the various field validation
// values.
Buckets: []float64{0.05, 0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 1.25, 1.5, 2, 3,
4, 5, 6, 8, 10, 15, 20, 30, 45, 60},
StabilityLevel: compbasemetrics.ALPHA,
},
[]string{"field_validation", "enabled"},
)
responseSizes = compbasemetrics.NewHistogramVec(
&compbasemetrics.HistogramOpts{
Name: "apiserver_response_sizes",
@ -261,6 +274,7 @@ var (
longRunningRequestGauge,
requestLatencies,
requestSloLatencies,
fieldValidationRequestLatencies,
responseSizes,
droppedRequests,
TLSHandshakeErrors,
@ -510,6 +524,10 @@ func MonitorRequest(req *http.Request, verb, group, version, resource, subresour
}
}
requestLatencies.WithContext(req.Context()).WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component).Observe(elapsedSeconds)
fieldValidation := cleanFieldValidation(req.URL)
fieldValidationEnabled := strconv.FormatBool(utilfeature.DefaultFeatureGate.Enabled(features.ServerSideFieldValidation))
fieldValidationRequestLatencies.WithContext(req.Context()).WithLabelValues(fieldValidation, fieldValidationEnabled)
if wd, ok := request.LatencyTrackersFrom(req.Context()); ok {
sloLatency := elapsedSeconds - (wd.MutatingWebhookTracker.GetLatency() + wd.ValidatingWebhookTracker.GetLatency()).Seconds()
requestSloLatencies.WithContext(req.Context()).WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(sloLatency)
@ -650,6 +668,21 @@ func cleanDryRun(u *url.URL) string {
return strings.Join(utilsets.NewString(dryRun...).List(), ",")
}
func cleanFieldValidation(u *url.URL) string {
// avoid allocating when we don't see dryRun in the query
if !strings.Contains(u.RawQuery, "fieldValidation") {
return ""
}
fieldValidation := u.Query()["fieldValidation"]
if len(fieldValidation) != 1 {
return "invalid"
}
if errs := validation.ValidateFieldValidation(nil, fieldValidation[0]); len(errs) > 0 {
return "invalid"
}
return fieldValidation[0]
}
var _ http.ResponseWriter = (*ResponseWriterDelegator)(nil)
var _ responsewriter.UserProvidedDecorator = (*ResponseWriterDelegator)(nil)

View File

@ -204,6 +204,62 @@ func TestCleanScope(t *testing.T) {
}
}
func TestCleanFieldValidation(t *testing.T) {
testCases := []struct {
name string
url *url.URL
expectedFieldValidation string
}{
{
name: "empty field validation",
url: &url.URL{},
expectedFieldValidation: "",
},
{
name: "ignore field validation",
url: &url.URL{
RawQuery: "fieldValidation=Ignore",
},
expectedFieldValidation: "Ignore",
},
{
name: "warn field validation",
url: &url.URL{
RawQuery: "fieldValidation=Warn",
},
expectedFieldValidation: "Warn",
},
{
name: "strict field validation",
url: &url.URL{
RawQuery: "fieldValidation=Strict",
},
expectedFieldValidation: "Strict",
},
{
name: "invalid field validation",
url: &url.URL{
RawQuery: "fieldValidation=foo",
},
expectedFieldValidation: "invalid",
},
{
name: "multiple field validation",
url: &url.URL{
RawQuery: "fieldValidation=Strict&fieldValidation=Ignore",
},
expectedFieldValidation: "invalid",
},
}
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
if fieldValidation := cleanFieldValidation(test.url); fieldValidation != test.expectedFieldValidation {
t.Errorf("failed to clean field validation, expected: %s, got: %s", test.expectedFieldValidation, fieldValidation)
}
})
}
}
func TestResponseWriterDecorator(t *testing.T) {
decorator := &ResponseWriterDelegator{
ResponseWriter: &responsewriter.FakeResponseWriter{},