Update go dependencies
This commit is contained in:
parent
a46126a034
commit
3eafaa35a1
1108 changed files with 32555 additions and 83490 deletions
5
vendor/k8s.io/apiserver/pkg/endpoints/metrics/OWNERS
generated
vendored
Normal file
5
vendor/k8s.io/apiserver/pkg/endpoints/metrics/OWNERS
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
reviewers:
|
||||
- wojtek-t
|
||||
- jimmidyson
|
||||
525
vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go
generated
vendored
Normal file
525
vendor/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go
generated
vendored
Normal file
|
|
@ -0,0 +1,525 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
restful "github.com/emicklei/go-restful"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/validation"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilsets "k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
compbasemetrics "k8s.io/component-base/metrics"
|
||||
"k8s.io/component-base/metrics/legacyregistry"
|
||||
)
|
||||
|
||||
// resettableCollector is the interface implemented by prometheus.MetricVec
|
||||
// that can be used by Prometheus to collect metrics and reset their values.
|
||||
type resettableCollector interface {
|
||||
compbasemetrics.Registerable
|
||||
Reset()
|
||||
}
|
||||
|
||||
const (
|
||||
APIServerComponent string = "apiserver"
|
||||
)
|
||||
|
||||
/*
|
||||
* By default, all the following metrics are defined as falling under
|
||||
* ALPHA stability level https://github.com/kubernetes/enhancements/blob/master/keps/sig-instrumentation/20190404-kubernetes-control-plane-metrics-stability.md#stability-classes)
|
||||
*
|
||||
* Promoting the stability level of the metric is a responsibility of the component owner, since it
|
||||
* involves explicitly acknowledging support for the metric across multiple releases, in accordance with
|
||||
* the metric stability policy.
|
||||
*/
|
||||
var (
|
||||
// TODO(a-robinson): Add unit tests for the handling of these metrics once
|
||||
// the upstream library supports it.
|
||||
requestCounter = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_request_total",
|
||||
Help: "Counter of apiserver requests broken out for each verb, dry run value, group, version, resource, scope, component, and HTTP response contentType and code.",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
// The label_name contentType doesn't follow the label_name convention defined here:
|
||||
// https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/instrumentation.md
|
||||
// But changing it would break backwards compatibility. Future label_names
|
||||
// should be all lowercase and separated by underscores.
|
||||
[]string{"verb", "dry_run", "group", "version", "resource", "subresource", "scope", "component", "contentType", "code"},
|
||||
)
|
||||
longRunningRequestGauge = compbasemetrics.NewGaugeVec(
|
||||
&compbasemetrics.GaugeOpts{
|
||||
Name: "apiserver_longrunning_gauge",
|
||||
Help: "Gauge of all active long-running apiserver requests broken out by verb, group, version, resource, scope and component. Not all requests are tracked this way.",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
requestLatencies = compbasemetrics.NewHistogramVec(
|
||||
&compbasemetrics.HistogramOpts{
|
||||
Name: "apiserver_request_duration_seconds",
|
||||
Help: "Response latency distribution in seconds for each verb, dry run value, group, version, resource, subresource, scope and component.",
|
||||
// This metric is used for verifying api call latencies SLO,
|
||||
// as well as tracking regressions in this aspects.
|
||||
// Thus we customize buckets significantly, to empower both usecases.
|
||||
Buckets: []float64{0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.35, 0.4, 0.45, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0,
|
||||
1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5, 6, 7, 8, 9, 10, 15, 20, 25, 30, 40, 50, 60},
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"verb", "dry_run", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
responseSizes = compbasemetrics.NewHistogramVec(
|
||||
&compbasemetrics.HistogramOpts{
|
||||
Name: "apiserver_response_sizes",
|
||||
Help: "Response size distribution in bytes for each group, version, verb, resource, subresource, scope and component.",
|
||||
// Use buckets ranging from 1000 bytes (1KB) to 10^9 bytes (1GB).
|
||||
Buckets: compbasemetrics.ExponentialBuckets(1000, 10.0, 7),
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component"},
|
||||
)
|
||||
// DroppedRequests is a number of requests dropped with 'Try again later' response"
|
||||
DroppedRequests = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_dropped_requests_total",
|
||||
Help: "Number of requests dropped with 'Try again later' response",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"requestKind"},
|
||||
)
|
||||
// RegisteredWatchers is a number of currently registered watchers splitted by resource.
|
||||
RegisteredWatchers = compbasemetrics.NewGaugeVec(
|
||||
&compbasemetrics.GaugeOpts{
|
||||
Name: "apiserver_registered_watchers",
|
||||
Help: "Number of currently registered watchers for a given resources",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"group", "version", "kind"},
|
||||
)
|
||||
WatchEvents = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_watch_events_total",
|
||||
Help: "Number of events sent in watch clients",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"group", "version", "kind"},
|
||||
)
|
||||
WatchEventsSizes = compbasemetrics.NewHistogramVec(
|
||||
&compbasemetrics.HistogramOpts{
|
||||
Name: "apiserver_watch_events_sizes",
|
||||
Help: "Watch event size distribution in bytes",
|
||||
Buckets: compbasemetrics.ExponentialBuckets(1024, 2.0, 8), // 1K, 2K, 4K, 8K, ..., 128K.
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"group", "version", "kind"},
|
||||
)
|
||||
// Because of volatility of the base metric this is pre-aggregated one. Instead of reporting current usage all the time
|
||||
// it reports maximal usage during the last second.
|
||||
currentInflightRequests = compbasemetrics.NewGaugeVec(
|
||||
&compbasemetrics.GaugeOpts{
|
||||
Name: "apiserver_current_inflight_requests",
|
||||
Help: "Maximal number of currently used inflight request limit of this apiserver per request kind in last second.",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"requestKind"},
|
||||
)
|
||||
|
||||
requestTerminationsTotal = compbasemetrics.NewCounterVec(
|
||||
&compbasemetrics.CounterOpts{
|
||||
Name: "apiserver_request_terminations_total",
|
||||
Help: "Number of requests which apiserver terminated in self-defense.",
|
||||
StabilityLevel: compbasemetrics.ALPHA,
|
||||
},
|
||||
[]string{"verb", "group", "version", "resource", "subresource", "scope", "component", "code"},
|
||||
)
|
||||
kubectlExeRegexp = regexp.MustCompile(`^.*((?i:kubectl\.exe))`)
|
||||
|
||||
metrics = []resettableCollector{
|
||||
requestCounter,
|
||||
longRunningRequestGauge,
|
||||
requestLatencies,
|
||||
responseSizes,
|
||||
DroppedRequests,
|
||||
RegisteredWatchers,
|
||||
WatchEvents,
|
||||
WatchEventsSizes,
|
||||
currentInflightRequests,
|
||||
requestTerminationsTotal,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
// ReadOnlyKind is a string identifying read only request kind
|
||||
ReadOnlyKind = "readOnly"
|
||||
// MutatingKind is a string identifying mutating request kind
|
||||
MutatingKind = "mutating"
|
||||
)
|
||||
|
||||
var registerMetrics sync.Once
|
||||
|
||||
// Register all metrics.
|
||||
func Register() {
|
||||
registerMetrics.Do(func() {
|
||||
for _, metric := range metrics {
|
||||
legacyregistry.MustRegister(metric)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Reset all metrics.
|
||||
func Reset() {
|
||||
for _, metric := range metrics {
|
||||
metric.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateInflightRequestMetrics(nonmutating, mutating int) {
|
||||
currentInflightRequests.WithLabelValues(ReadOnlyKind).Set(float64(nonmutating))
|
||||
currentInflightRequests.WithLabelValues(MutatingKind).Set(float64(mutating))
|
||||
}
|
||||
|
||||
// RecordRequestTermination records that the request was terminated early as part of a resource
|
||||
// preservation or apiserver self-defense mechanism (e.g. timeouts, maxinflight throttling,
|
||||
// proxyHandler errors). RecordRequestTermination should only be called zero or one times
|
||||
// per request.
|
||||
func RecordRequestTermination(req *http.Request, requestInfo *request.RequestInfo, component string, code int) {
|
||||
if requestInfo == nil {
|
||||
requestInfo = &request.RequestInfo{Verb: req.Method, Path: req.URL.Path}
|
||||
}
|
||||
scope := CleanScope(requestInfo)
|
||||
// We don't use verb from <requestInfo>, as for the healthy path
|
||||
// MonitorRequest is called from InstrumentRouteFunc which is registered
|
||||
// in installer.go with predefined list of verbs (different than those
|
||||
// translated to RequestInfo).
|
||||
// However, we need to tweak it e.g. to differentiate GET from LIST.
|
||||
verb := canonicalVerb(strings.ToUpper(req.Method), scope)
|
||||
if requestInfo.IsResourceRequest {
|
||||
requestTerminationsTotal.WithLabelValues(cleanVerb(verb, req), requestInfo.APIGroup, requestInfo.APIVersion, requestInfo.Resource, requestInfo.Subresource, scope, component, codeToString(code)).Inc()
|
||||
} else {
|
||||
requestTerminationsTotal.WithLabelValues(cleanVerb(verb, req), "", "", "", requestInfo.Path, scope, component, codeToString(code)).Inc()
|
||||
}
|
||||
}
|
||||
|
||||
// RecordLongRunning tracks the execution of a long running request against the API server. It provides an accurate count
|
||||
// of the total number of open long running requests. requestInfo may be nil if the caller is not in the normal request flow.
|
||||
func RecordLongRunning(req *http.Request, requestInfo *request.RequestInfo, component string, fn func()) {
|
||||
if requestInfo == nil {
|
||||
requestInfo = &request.RequestInfo{Verb: req.Method, Path: req.URL.Path}
|
||||
}
|
||||
var g compbasemetrics.GaugeMetric
|
||||
scope := CleanScope(requestInfo)
|
||||
// We don't use verb from <requestInfo>, as for the healthy path
|
||||
// MonitorRequest is called from InstrumentRouteFunc which is registered
|
||||
// in installer.go with predefined list of verbs (different than those
|
||||
// translated to RequestInfo).
|
||||
// However, we need to tweak it e.g. to differentiate GET from LIST.
|
||||
reportedVerb := cleanVerb(canonicalVerb(strings.ToUpper(req.Method), scope), req)
|
||||
if requestInfo.IsResourceRequest {
|
||||
g = longRunningRequestGauge.WithLabelValues(reportedVerb, requestInfo.APIGroup, requestInfo.APIVersion, requestInfo.Resource, requestInfo.Subresource, scope, component)
|
||||
} else {
|
||||
g = longRunningRequestGauge.WithLabelValues(reportedVerb, "", "", "", requestInfo.Path, scope, component)
|
||||
}
|
||||
g.Inc()
|
||||
defer g.Dec()
|
||||
fn()
|
||||
}
|
||||
|
||||
// MonitorRequest handles standard transformations for client and the reported verb and then invokes Monitor to record
|
||||
// a request. verb must be uppercase to be backwards compatible with existing monitoring tooling.
|
||||
func MonitorRequest(req *http.Request, verb, group, version, resource, subresource, scope, component, contentType string, httpCode, respSize int, elapsed time.Duration) {
|
||||
reportedVerb := cleanVerb(verb, req)
|
||||
dryRun := cleanDryRun(req.URL)
|
||||
elapsedSeconds := elapsed.Seconds()
|
||||
requestCounter.WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component, contentType, codeToString(httpCode)).Inc()
|
||||
requestLatencies.WithLabelValues(reportedVerb, dryRun, group, version, resource, subresource, scope, component).Observe(elapsedSeconds)
|
||||
// We are only interested in response sizes of read requests.
|
||||
if verb == "GET" || verb == "LIST" {
|
||||
responseSizes.WithLabelValues(reportedVerb, group, version, resource, subresource, scope, component).Observe(float64(respSize))
|
||||
}
|
||||
}
|
||||
|
||||
// InstrumentRouteFunc works like Prometheus' InstrumentHandlerFunc but wraps
|
||||
// the go-restful RouteFunction instead of a HandlerFunc plus some Kubernetes endpoint specific information.
|
||||
func InstrumentRouteFunc(verb, group, version, resource, subresource, scope, component string, routeFunc restful.RouteFunction) restful.RouteFunction {
|
||||
return restful.RouteFunction(func(request *restful.Request, response *restful.Response) {
|
||||
now := time.Now()
|
||||
|
||||
delegate := &ResponseWriterDelegator{ResponseWriter: response.ResponseWriter}
|
||||
|
||||
_, cn := response.ResponseWriter.(http.CloseNotifier)
|
||||
_, fl := response.ResponseWriter.(http.Flusher)
|
||||
_, hj := response.ResponseWriter.(http.Hijacker)
|
||||
var rw http.ResponseWriter
|
||||
if cn && fl && hj {
|
||||
rw = &fancyResponseWriterDelegator{delegate}
|
||||
} else {
|
||||
rw = delegate
|
||||
}
|
||||
response.ResponseWriter = rw
|
||||
|
||||
routeFunc(request, response)
|
||||
|
||||
MonitorRequest(request.Request, verb, group, version, resource, subresource, scope, component, delegate.Header().Get("Content-Type"), delegate.Status(), delegate.ContentLength(), time.Since(now))
|
||||
})
|
||||
}
|
||||
|
||||
// InstrumentHandlerFunc works like Prometheus' InstrumentHandlerFunc but adds some Kubernetes endpoint specific information.
|
||||
func InstrumentHandlerFunc(verb, group, version, resource, subresource, scope, component string, handler http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, req *http.Request) {
|
||||
now := time.Now()
|
||||
|
||||
delegate := &ResponseWriterDelegator{ResponseWriter: w}
|
||||
|
||||
_, cn := w.(http.CloseNotifier)
|
||||
_, fl := w.(http.Flusher)
|
||||
_, hj := w.(http.Hijacker)
|
||||
if cn && fl && hj {
|
||||
w = &fancyResponseWriterDelegator{delegate}
|
||||
} else {
|
||||
w = delegate
|
||||
}
|
||||
|
||||
handler(w, req)
|
||||
|
||||
MonitorRequest(req, verb, group, version, resource, subresource, scope, component, delegate.Header().Get("Content-Type"), delegate.Status(), delegate.ContentLength(), time.Since(now))
|
||||
}
|
||||
}
|
||||
|
||||
// CleanScope returns the scope of the request.
|
||||
func CleanScope(requestInfo *request.RequestInfo) string {
|
||||
if requestInfo.Namespace != "" {
|
||||
return "namespace"
|
||||
}
|
||||
if requestInfo.Name != "" {
|
||||
return "resource"
|
||||
}
|
||||
if requestInfo.IsResourceRequest {
|
||||
return "cluster"
|
||||
}
|
||||
// this is the empty scope
|
||||
return ""
|
||||
}
|
||||
|
||||
func canonicalVerb(verb string, scope string) string {
|
||||
switch verb {
|
||||
case "GET", "HEAD":
|
||||
if scope != "resource" {
|
||||
return "LIST"
|
||||
}
|
||||
return "GET"
|
||||
default:
|
||||
return verb
|
||||
}
|
||||
}
|
||||
|
||||
func cleanVerb(verb string, request *http.Request) string {
|
||||
reportedVerb := verb
|
||||
if verb == "LIST" {
|
||||
// see apimachinery/pkg/runtime/conversion.go Convert_Slice_string_To_bool
|
||||
if values := request.URL.Query()["watch"]; len(values) > 0 {
|
||||
if value := strings.ToLower(values[0]); value != "0" && value != "false" {
|
||||
reportedVerb = "WATCH"
|
||||
}
|
||||
}
|
||||
}
|
||||
// normalize the legacy WATCHLIST to WATCH to ensure users aren't surprised by metrics
|
||||
if verb == "WATCHLIST" {
|
||||
reportedVerb = "WATCH"
|
||||
}
|
||||
if verb == "PATCH" && request.Header.Get("Content-Type") == string(types.ApplyPatchType) && utilfeature.DefaultFeatureGate.Enabled(features.ServerSideApply) {
|
||||
reportedVerb = "APPLY"
|
||||
}
|
||||
return reportedVerb
|
||||
}
|
||||
|
||||
func cleanDryRun(u *url.URL) string {
|
||||
// avoid allocating when we don't see dryRun in the query
|
||||
if !strings.Contains(u.RawQuery, "dryRun") {
|
||||
return ""
|
||||
}
|
||||
dryRun := u.Query()["dryRun"]
|
||||
if errs := validation.ValidateDryRun(nil, dryRun); len(errs) > 0 {
|
||||
return "invalid"
|
||||
}
|
||||
// Since dryRun could be valid with any arbitrarily long length
|
||||
// we have to dedup and sort the elements before joining them together
|
||||
// TODO: this is a fairly large allocation for what it does, consider
|
||||
// a sort and dedup in a single pass
|
||||
return strings.Join(utilsets.NewString(dryRun...).List(), ",")
|
||||
}
|
||||
|
||||
// ResponseWriterDelegator interface wraps http.ResponseWriter to additionally record content-length, status-code, etc.
|
||||
type ResponseWriterDelegator struct {
|
||||
http.ResponseWriter
|
||||
|
||||
status int
|
||||
written int64
|
||||
wroteHeader bool
|
||||
}
|
||||
|
||||
func (r *ResponseWriterDelegator) WriteHeader(code int) {
|
||||
r.status = code
|
||||
r.wroteHeader = true
|
||||
r.ResponseWriter.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (r *ResponseWriterDelegator) Write(b []byte) (int, error) {
|
||||
if !r.wroteHeader {
|
||||
r.WriteHeader(http.StatusOK)
|
||||
}
|
||||
n, err := r.ResponseWriter.Write(b)
|
||||
r.written += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (r *ResponseWriterDelegator) Status() int {
|
||||
return r.status
|
||||
}
|
||||
|
||||
func (r *ResponseWriterDelegator) ContentLength() int {
|
||||
return int(r.written)
|
||||
}
|
||||
|
||||
type fancyResponseWriterDelegator struct {
|
||||
*ResponseWriterDelegator
|
||||
}
|
||||
|
||||
func (f *fancyResponseWriterDelegator) CloseNotify() <-chan bool {
|
||||
return f.ResponseWriter.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func (f *fancyResponseWriterDelegator) Flush() {
|
||||
f.ResponseWriter.(http.Flusher).Flush()
|
||||
}
|
||||
|
||||
func (f *fancyResponseWriterDelegator) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return f.ResponseWriter.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
// Small optimization over Itoa
|
||||
func codeToString(s int) string {
|
||||
switch s {
|
||||
case 100:
|
||||
return "100"
|
||||
case 101:
|
||||
return "101"
|
||||
|
||||
case 200:
|
||||
return "200"
|
||||
case 201:
|
||||
return "201"
|
||||
case 202:
|
||||
return "202"
|
||||
case 203:
|
||||
return "203"
|
||||
case 204:
|
||||
return "204"
|
||||
case 205:
|
||||
return "205"
|
||||
case 206:
|
||||
return "206"
|
||||
|
||||
case 300:
|
||||
return "300"
|
||||
case 301:
|
||||
return "301"
|
||||
case 302:
|
||||
return "302"
|
||||
case 304:
|
||||
return "304"
|
||||
case 305:
|
||||
return "305"
|
||||
case 307:
|
||||
return "307"
|
||||
|
||||
case 400:
|
||||
return "400"
|
||||
case 401:
|
||||
return "401"
|
||||
case 402:
|
||||
return "402"
|
||||
case 403:
|
||||
return "403"
|
||||
case 404:
|
||||
return "404"
|
||||
case 405:
|
||||
return "405"
|
||||
case 406:
|
||||
return "406"
|
||||
case 407:
|
||||
return "407"
|
||||
case 408:
|
||||
return "408"
|
||||
case 409:
|
||||
return "409"
|
||||
case 410:
|
||||
return "410"
|
||||
case 411:
|
||||
return "411"
|
||||
case 412:
|
||||
return "412"
|
||||
case 413:
|
||||
return "413"
|
||||
case 414:
|
||||
return "414"
|
||||
case 415:
|
||||
return "415"
|
||||
case 416:
|
||||
return "416"
|
||||
case 417:
|
||||
return "417"
|
||||
case 418:
|
||||
return "418"
|
||||
|
||||
case 500:
|
||||
return "500"
|
||||
case 501:
|
||||
return "501"
|
||||
case 502:
|
||||
return "502"
|
||||
case 503:
|
||||
return "503"
|
||||
case 504:
|
||||
return "504"
|
||||
case 505:
|
||||
return "505"
|
||||
|
||||
case 428:
|
||||
return "428"
|
||||
case 429:
|
||||
return "429"
|
||||
case 431:
|
||||
return "431"
|
||||
case 511:
|
||||
return "511"
|
||||
|
||||
default:
|
||||
return strconv.Itoa(s)
|
||||
}
|
||||
}
|
||||
4
vendor/k8s.io/apiserver/pkg/endpoints/request/OWNERS
generated
vendored
Normal file
4
vendor/k8s.io/apiserver/pkg/endpoints/request/OWNERS
generated
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
reviewers:
|
||||
- sttts
|
||||
96
vendor/k8s.io/apiserver/pkg/endpoints/request/context.go
generated
vendored
Normal file
96
vendor/k8s.io/apiserver/pkg/endpoints/request/context.go
generated
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/apis/audit"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
)
|
||||
|
||||
// The key type is unexported to prevent collisions
|
||||
type key int
|
||||
|
||||
const (
|
||||
// namespaceKey is the context key for the request namespace.
|
||||
namespaceKey key = iota
|
||||
|
||||
// userKey is the context key for the request user.
|
||||
userKey
|
||||
|
||||
// auditKey is the context key for the audit event.
|
||||
auditKey
|
||||
|
||||
// audiencesKey is the context key for request audiences.
|
||||
audiencesKey
|
||||
)
|
||||
|
||||
// NewContext instantiates a base context object for request flows.
|
||||
func NewContext() context.Context {
|
||||
return context.TODO()
|
||||
}
|
||||
|
||||
// NewDefaultContext instantiates a base context object for request flows in the default namespace
|
||||
func NewDefaultContext() context.Context {
|
||||
return WithNamespace(NewContext(), metav1.NamespaceDefault)
|
||||
}
|
||||
|
||||
// WithValue returns a copy of parent in which the value associated with key is val.
|
||||
func WithValue(parent context.Context, key interface{}, val interface{}) context.Context {
|
||||
return context.WithValue(parent, key, val)
|
||||
}
|
||||
|
||||
// WithNamespace returns a copy of parent in which the namespace value is set
|
||||
func WithNamespace(parent context.Context, namespace string) context.Context {
|
||||
return WithValue(parent, namespaceKey, namespace)
|
||||
}
|
||||
|
||||
// NamespaceFrom returns the value of the namespace key on the ctx
|
||||
func NamespaceFrom(ctx context.Context) (string, bool) {
|
||||
namespace, ok := ctx.Value(namespaceKey).(string)
|
||||
return namespace, ok
|
||||
}
|
||||
|
||||
// NamespaceValue returns the value of the namespace key on the ctx, or the empty string if none
|
||||
func NamespaceValue(ctx context.Context) string {
|
||||
namespace, _ := NamespaceFrom(ctx)
|
||||
return namespace
|
||||
}
|
||||
|
||||
// WithUser returns a copy of parent in which the user value is set
|
||||
func WithUser(parent context.Context, user user.Info) context.Context {
|
||||
return WithValue(parent, userKey, user)
|
||||
}
|
||||
|
||||
// UserFrom returns the value of the user key on the ctx
|
||||
func UserFrom(ctx context.Context) (user.Info, bool) {
|
||||
user, ok := ctx.Value(userKey).(user.Info)
|
||||
return user, ok
|
||||
}
|
||||
|
||||
// WithAuditEvent returns set audit event struct.
|
||||
func WithAuditEvent(parent context.Context, ev *audit.Event) context.Context {
|
||||
return WithValue(parent, auditKey, ev)
|
||||
}
|
||||
|
||||
// AuditEventFrom returns the audit event struct on the ctx
|
||||
func AuditEventFrom(ctx context.Context) *audit.Event {
|
||||
ev, _ := ctx.Value(auditKey).(*audit.Event)
|
||||
return ev
|
||||
}
|
||||
20
vendor/k8s.io/apiserver/pkg/endpoints/request/doc.go
generated
vendored
Normal file
20
vendor/k8s.io/apiserver/pkg/endpoints/request/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package request contains everything around extracting info from
|
||||
// a http request object.
|
||||
// TODO: this package is temporary. Handlers must move into pkg/apiserver/handlers to avoid dependency cycle
|
||||
package request // import "k8s.io/apiserver/pkg/endpoints/request"
|
||||
274
vendor/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go
generated
vendored
Normal file
274
vendor/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go
generated
vendored
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
/*
|
||||
Copyright 2016 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package request
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/validation/path"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metainternalversionscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// LongRunningRequestCheck is a predicate which is true for long-running http requests.
|
||||
type LongRunningRequestCheck func(r *http.Request, requestInfo *RequestInfo) bool
|
||||
|
||||
type RequestInfoResolver interface {
|
||||
NewRequestInfo(req *http.Request) (*RequestInfo, error)
|
||||
}
|
||||
|
||||
// RequestInfo holds information parsed from the http.Request
|
||||
type RequestInfo struct {
|
||||
// IsResourceRequest indicates whether or not the request is for an API resource or subresource
|
||||
IsResourceRequest bool
|
||||
// Path is the URL path of the request
|
||||
Path string
|
||||
// Verb is the kube verb associated with the request for API requests, not the http verb. This includes things like list and watch.
|
||||
// for non-resource requests, this is the lowercase http verb
|
||||
Verb string
|
||||
|
||||
APIPrefix string
|
||||
APIGroup string
|
||||
APIVersion string
|
||||
Namespace string
|
||||
// Resource is the name of the resource being requested. This is not the kind. For example: pods
|
||||
Resource string
|
||||
// Subresource is the name of the subresource being requested. This is a different resource, scoped to the parent resource, but it may have a different kind.
|
||||
// For instance, /pods has the resource "pods" and the kind "Pod", while /pods/foo/status has the resource "pods", the sub resource "status", and the kind "Pod"
|
||||
// (because status operates on pods). The binding resource for a pod though may be /pods/foo/binding, which has resource "pods", subresource "binding", and kind "Binding".
|
||||
Subresource string
|
||||
// Name is empty for some verbs, but if the request directly indicates a name (not in body content) then this field is filled in.
|
||||
Name string
|
||||
// Parts are the path parts for the request, always starting with /{resource}/{name}
|
||||
Parts []string
|
||||
}
|
||||
|
||||
// specialVerbs contains just strings which are used in REST paths for special actions that don't fall under the normal
|
||||
// CRUDdy GET/POST/PUT/DELETE actions on REST objects.
|
||||
// TODO: find a way to keep this up to date automatically. Maybe dynamically populate list as handlers added to
|
||||
// master's Mux.
|
||||
var specialVerbs = sets.NewString("proxy", "watch")
|
||||
|
||||
// specialVerbsNoSubresources contains root verbs which do not allow subresources
|
||||
var specialVerbsNoSubresources = sets.NewString("proxy")
|
||||
|
||||
// namespaceSubresources contains subresources of namespace
|
||||
// this list allows the parser to distinguish between a namespace subresource, and a namespaced resource
|
||||
var namespaceSubresources = sets.NewString("status", "finalize")
|
||||
|
||||
// NamespaceSubResourcesForTest exports namespaceSubresources for testing in pkg/master/master_test.go, so we never drift
|
||||
var NamespaceSubResourcesForTest = sets.NewString(namespaceSubresources.List()...)
|
||||
|
||||
type RequestInfoFactory struct {
|
||||
APIPrefixes sets.String // without leading and trailing slashes
|
||||
GrouplessAPIPrefixes sets.String // without leading and trailing slashes
|
||||
}
|
||||
|
||||
// TODO write an integration test against the swagger doc to test the RequestInfo and match up behavior to responses
|
||||
// NewRequestInfo returns the information from the http request. If error is not nil, RequestInfo holds the information as best it is known before the failure
|
||||
// It handles both resource and non-resource requests and fills in all the pertinent information for each.
|
||||
// Valid Inputs:
|
||||
// Resource paths
|
||||
// /apis/{api-group}/{version}/namespaces
|
||||
// /api/{version}/namespaces
|
||||
// /api/{version}/namespaces/{namespace}
|
||||
// /api/{version}/namespaces/{namespace}/{resource}
|
||||
// /api/{version}/namespaces/{namespace}/{resource}/{resourceName}
|
||||
// /api/{version}/{resource}
|
||||
// /api/{version}/{resource}/{resourceName}
|
||||
//
|
||||
// Special verbs without subresources:
|
||||
// /api/{version}/proxy/{resource}/{resourceName}
|
||||
// /api/{version}/proxy/namespaces/{namespace}/{resource}/{resourceName}
|
||||
//
|
||||
// Special verbs with subresources:
|
||||
// /api/{version}/watch/{resource}
|
||||
// /api/{version}/watch/namespaces/{namespace}/{resource}
|
||||
//
|
||||
// NonResource paths
|
||||
// /apis/{api-group}/{version}
|
||||
// /apis/{api-group}
|
||||
// /apis
|
||||
// /api/{version}
|
||||
// /api
|
||||
// /healthz
|
||||
// /
|
||||
func (r *RequestInfoFactory) NewRequestInfo(req *http.Request) (*RequestInfo, error) {
|
||||
// start with a non-resource request until proven otherwise
|
||||
requestInfo := RequestInfo{
|
||||
IsResourceRequest: false,
|
||||
Path: req.URL.Path,
|
||||
Verb: strings.ToLower(req.Method),
|
||||
}
|
||||
|
||||
currentParts := splitPath(req.URL.Path)
|
||||
if len(currentParts) < 3 {
|
||||
// return a non-resource request
|
||||
return &requestInfo, nil
|
||||
}
|
||||
|
||||
if !r.APIPrefixes.Has(currentParts[0]) {
|
||||
// return a non-resource request
|
||||
return &requestInfo, nil
|
||||
}
|
||||
requestInfo.APIPrefix = currentParts[0]
|
||||
currentParts = currentParts[1:]
|
||||
|
||||
if !r.GrouplessAPIPrefixes.Has(requestInfo.APIPrefix) {
|
||||
// one part (APIPrefix) has already been consumed, so this is actually "do we have four parts?"
|
||||
if len(currentParts) < 3 {
|
||||
// return a non-resource request
|
||||
return &requestInfo, nil
|
||||
}
|
||||
|
||||
requestInfo.APIGroup = currentParts[0]
|
||||
currentParts = currentParts[1:]
|
||||
}
|
||||
|
||||
requestInfo.IsResourceRequest = true
|
||||
requestInfo.APIVersion = currentParts[0]
|
||||
currentParts = currentParts[1:]
|
||||
|
||||
// handle input of form /{specialVerb}/*
|
||||
if specialVerbs.Has(currentParts[0]) {
|
||||
if len(currentParts) < 2 {
|
||||
return &requestInfo, fmt.Errorf("unable to determine kind and namespace from url, %v", req.URL)
|
||||
}
|
||||
|
||||
requestInfo.Verb = currentParts[0]
|
||||
currentParts = currentParts[1:]
|
||||
|
||||
} else {
|
||||
switch req.Method {
|
||||
case "POST":
|
||||
requestInfo.Verb = "create"
|
||||
case "GET", "HEAD":
|
||||
requestInfo.Verb = "get"
|
||||
case "PUT":
|
||||
requestInfo.Verb = "update"
|
||||
case "PATCH":
|
||||
requestInfo.Verb = "patch"
|
||||
case "DELETE":
|
||||
requestInfo.Verb = "delete"
|
||||
default:
|
||||
requestInfo.Verb = ""
|
||||
}
|
||||
}
|
||||
|
||||
// URL forms: /namespaces/{namespace}/{kind}/*, where parts are adjusted to be relative to kind
|
||||
if currentParts[0] == "namespaces" {
|
||||
if len(currentParts) > 1 {
|
||||
requestInfo.Namespace = currentParts[1]
|
||||
|
||||
// if there is another step after the namespace name and it is not a known namespace subresource
|
||||
// move currentParts to include it as a resource in its own right
|
||||
if len(currentParts) > 2 && !namespaceSubresources.Has(currentParts[2]) {
|
||||
currentParts = currentParts[2:]
|
||||
}
|
||||
}
|
||||
} else {
|
||||
requestInfo.Namespace = metav1.NamespaceNone
|
||||
}
|
||||
|
||||
// parsing successful, so we now know the proper value for .Parts
|
||||
requestInfo.Parts = currentParts
|
||||
|
||||
// parts look like: resource/resourceName/subresource/other/stuff/we/don't/interpret
|
||||
switch {
|
||||
case len(requestInfo.Parts) >= 3 && !specialVerbsNoSubresources.Has(requestInfo.Verb):
|
||||
requestInfo.Subresource = requestInfo.Parts[2]
|
||||
fallthrough
|
||||
case len(requestInfo.Parts) >= 2:
|
||||
requestInfo.Name = requestInfo.Parts[1]
|
||||
fallthrough
|
||||
case len(requestInfo.Parts) >= 1:
|
||||
requestInfo.Resource = requestInfo.Parts[0]
|
||||
}
|
||||
|
||||
// if there's no name on the request and we thought it was a get before, then the actual verb is a list or a watch
|
||||
if len(requestInfo.Name) == 0 && requestInfo.Verb == "get" {
|
||||
opts := metainternalversion.ListOptions{}
|
||||
if err := metainternalversionscheme.ParameterCodec.DecodeParameters(req.URL.Query(), metav1.SchemeGroupVersion, &opts); err != nil {
|
||||
// An error in parsing request will result in default to "list" and not setting "name" field.
|
||||
klog.Errorf("Couldn't parse request %#v: %v", req.URL.Query(), err)
|
||||
// Reset opts to not rely on partial results from parsing.
|
||||
// However, if watch is set, let's report it.
|
||||
opts = metainternalversion.ListOptions{}
|
||||
if values := req.URL.Query()["watch"]; len(values) > 0 {
|
||||
switch strings.ToLower(values[0]) {
|
||||
case "false", "0":
|
||||
default:
|
||||
opts.Watch = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if opts.Watch {
|
||||
requestInfo.Verb = "watch"
|
||||
} else {
|
||||
requestInfo.Verb = "list"
|
||||
}
|
||||
|
||||
if opts.FieldSelector != nil {
|
||||
if name, ok := opts.FieldSelector.RequiresExactMatch("metadata.name"); ok {
|
||||
if len(path.IsValidPathSegmentName(name)) == 0 {
|
||||
requestInfo.Name = name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if there's no name on the request and we thought it was a delete before, then the actual verb is deletecollection
|
||||
if len(requestInfo.Name) == 0 && requestInfo.Verb == "delete" {
|
||||
requestInfo.Verb = "deletecollection"
|
||||
}
|
||||
|
||||
return &requestInfo, nil
|
||||
}
|
||||
|
||||
type requestInfoKeyType int
|
||||
|
||||
// requestInfoKey is the RequestInfo key for the context. It's of private type here. Because
|
||||
// keys are interfaces and interfaces are equal when the type and the value is equal, this
|
||||
// does not conflict with the keys defined in pkg/api.
|
||||
const requestInfoKey requestInfoKeyType = iota
|
||||
|
||||
// WithRequestInfo returns a copy of parent in which the request info value is set
|
||||
func WithRequestInfo(parent context.Context, info *RequestInfo) context.Context {
|
||||
return WithValue(parent, requestInfoKey, info)
|
||||
}
|
||||
|
||||
// RequestInfoFrom returns the value of the RequestInfo key on the ctx
|
||||
func RequestInfoFrom(ctx context.Context) (*RequestInfo, bool) {
|
||||
info, ok := ctx.Value(requestInfoKey).(*RequestInfo)
|
||||
return info, ok
|
||||
}
|
||||
|
||||
// splitPath returns the segments for a URL path.
|
||||
func splitPath(path string) []string {
|
||||
path = strings.Trim(path, "/")
|
||||
if path == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(path, "/")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue