Switch to go modules
This commit is contained in:
parent
461954facb
commit
1720059244
763 changed files with 24896 additions and 177398 deletions
10
vendor/k8s.io/apiserver/.import-restrictions
generated
vendored
10
vendor/k8s.io/apiserver/.import-restrictions
generated
vendored
|
|
@ -1,10 +0,0 @@
|
|||
{
|
||||
"Rules": [
|
||||
{
|
||||
"SelectorRegexp": "k8s[.]io/kubernetes",
|
||||
"ForbiddenPrefixes": [
|
||||
""
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
7
vendor/k8s.io/apiserver/CONTRIBUTING.md
generated
vendored
7
vendor/k8s.io/apiserver/CONTRIBUTING.md
generated
vendored
|
|
@ -1,7 +0,0 @@
|
|||
# Contributing guidelines
|
||||
|
||||
Do not open pull requests directly against this repository, they will be ignored. Instead, please open pull requests against [kubernetes/kubernetes](https://git.k8s.io/kubernetes/). Please follow the same [contributing guide](https://git.k8s.io/kubernetes/CONTRIBUTING.md) you would follow for any other pull request made to kubernetes/kubernetes.
|
||||
|
||||
This repository is published from [kubernetes/kubernetes/staging/src/k8s.io/apiserver](https://git.k8s.io/kubernetes/staging/src/k8s.io/apiserver) by the [kubernetes publishing-bot](https://git.k8s.io/publishing-bot).
|
||||
|
||||
Please see [Staging Directory and Publishing](https://git.k8s.io/community/contributors/devel/sig-architecture/staging.md) for more information
|
||||
26
vendor/k8s.io/apiserver/OWNERS
generated
vendored
26
vendor/k8s.io/apiserver/OWNERS
generated
vendored
|
|
@ -1,26 +0,0 @@
|
|||
# See the OWNERS docs at https://go.k8s.io/owners
|
||||
|
||||
approvers:
|
||||
- lavalamp
|
||||
- smarterclayton
|
||||
- deads2k
|
||||
- sttts
|
||||
- liggitt
|
||||
reviewers:
|
||||
- lavalamp
|
||||
- smarterclayton
|
||||
- wojtek-t
|
||||
- deads2k
|
||||
- caesarxuchao
|
||||
- liggitt
|
||||
- sttts
|
||||
- ncdc
|
||||
- tallclair
|
||||
- enj
|
||||
- hzxuzhonghu
|
||||
- CaoShuFeng
|
||||
- jennybuckley
|
||||
- apelisse
|
||||
labels:
|
||||
- sig/api-machinery
|
||||
- area/apiserver
|
||||
30
vendor/k8s.io/apiserver/README.md
generated
vendored
30
vendor/k8s.io/apiserver/README.md
generated
vendored
|
|
@ -1,30 +0,0 @@
|
|||
# apiserver
|
||||
|
||||
Generic library for building a Kubernetes aggregated API server.
|
||||
|
||||
|
||||
## Purpose
|
||||
|
||||
This library contains code to create Kubernetes aggregation server complete with delegated authentication and authorization,
|
||||
`kubectl` compatible discovery information, optional admission chain, and versioned types. It's first consumers are
|
||||
`k8s.io/kubernetes`, `k8s.io/kube-aggregator`, and `github.com/kubernetes-incubator/service-catalog`.
|
||||
|
||||
|
||||
## Compatibility
|
||||
|
||||
There are *NO compatibility guarantees* for this repository, yet. It is in direct support of Kubernetes, so branches
|
||||
will track Kubernetes and be compatible with that repo. As we more cleanly separate the layers, we will review the
|
||||
compatibility guarantee. We have a goal to make this easier to use in the future.
|
||||
|
||||
|
||||
## Where does it come from?
|
||||
|
||||
`apiserver` is synced from https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver.
|
||||
Code changes are made in that location, merged into `k8s.io/kubernetes` and later synced here.
|
||||
|
||||
|
||||
## Things you should *NOT* do
|
||||
|
||||
1. Directly modify any files under `pkg` in this repo. Those are driven from `k8s.io/kubernetes/staging/src/k8s.io/apiserver`.
|
||||
2. Expect compatibility. This repo is changing quickly in direct support of
|
||||
Kubernetes and the API isn't yet stable enough for API guarantees.
|
||||
17
vendor/k8s.io/apiserver/SECURITY_CONTACTS
generated
vendored
17
vendor/k8s.io/apiserver/SECURITY_CONTACTS
generated
vendored
|
|
@ -1,17 +0,0 @@
|
|||
# Defined below are the security contacts for this repo.
|
||||
#
|
||||
# They are the contact point for the Product Security Committee to reach out
|
||||
# to for triaging and handling of incoming issues.
|
||||
#
|
||||
# The below names agree to abide by the
|
||||
# [Embargo Policy](https://git.k8s.io/security/private-distributors-list.md#embargo-policy)
|
||||
# and will be removed and replaced if they violate that agreement.
|
||||
#
|
||||
# DO NOT REPORT SECURITY VULNERABILITIES DIRECTLY TO THESE NAMES, FOLLOW THE
|
||||
# INSTRUCTIONS AT https://kubernetes.io/security/
|
||||
|
||||
cjcullen
|
||||
jessfraz
|
||||
liggitt
|
||||
philips
|
||||
tallclair
|
||||
3
vendor/k8s.io/apiserver/code-of-conduct.md
generated
vendored
3
vendor/k8s.io/apiserver/code-of-conduct.md
generated
vendored
|
|
@ -1,3 +0,0 @@
|
|||
# Kubernetes Community Code of Conduct
|
||||
|
||||
Please refer to our [Kubernetes Community Code of Conduct](https://git.k8s.io/community/code-of-conduct.md)
|
||||
637
vendor/k8s.io/apiserver/pkg/server/config.go
generated
vendored
637
vendor/k8s.io/apiserver/pkg/server/config.go
generated
vendored
|
|
@ -1,637 +0,0 @@
|
|||
/*
|
||||
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 server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
goruntime "runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/pborman/uuid"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
auditpolicy "k8s.io/apiserver/pkg/audit/policy"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||
authenticatorunion "k8s.io/apiserver/pkg/authentication/request/union"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
authorizerunion "k8s.io/apiserver/pkg/authorization/union"
|
||||
"k8s.io/apiserver/pkg/endpoints/discovery"
|
||||
genericapifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
||||
apiopenapi "k8s.io/apiserver/pkg/endpoints/openapi"
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic"
|
||||
genericfilters "k8s.io/apiserver/pkg/server/filters"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
"k8s.io/apiserver/pkg/server/routes"
|
||||
serverstore "k8s.io/apiserver/pkg/server/storage"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/client-go/informers"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
"k8s.io/component-base/logs"
|
||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
// install apis
|
||||
_ "k8s.io/apiserver/pkg/apis/apiserver/install"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultLegacyAPIPrefix is where the legacy APIs will be located.
|
||||
DefaultLegacyAPIPrefix = "/api"
|
||||
|
||||
// APIGroupPrefix is where non-legacy API group will be located.
|
||||
APIGroupPrefix = "/apis"
|
||||
)
|
||||
|
||||
// Config is a structure used to configure a GenericAPIServer.
|
||||
// Its members are sorted roughly in order of importance for composers.
|
||||
type Config struct {
|
||||
// SecureServing is required to serve https
|
||||
SecureServing *SecureServingInfo
|
||||
|
||||
// Authentication is the configuration for authentication
|
||||
Authentication AuthenticationInfo
|
||||
|
||||
// Authorization is the configuration for authorization
|
||||
Authorization AuthorizationInfo
|
||||
|
||||
// LoopbackClientConfig is a config for a privileged loopback connection to the API server
|
||||
// This is required for proper functioning of the PostStartHooks on a GenericAPIServer
|
||||
// TODO: move into SecureServing(WithLoopback) as soon as insecure serving is gone
|
||||
LoopbackClientConfig *restclient.Config
|
||||
// RuleResolver is required to get the list of rules that apply to a given user
|
||||
// in a given namespace
|
||||
RuleResolver authorizer.RuleResolver
|
||||
// AdmissionControl performs deep inspection of a given request (including content)
|
||||
// to set values and determine whether its allowed
|
||||
AdmissionControl admission.Interface
|
||||
CorsAllowedOriginList []string
|
||||
|
||||
EnableIndex bool
|
||||
EnableProfiling bool
|
||||
EnableDiscovery bool
|
||||
// Requires generic profiling enabled
|
||||
EnableContentionProfiling bool
|
||||
EnableMetrics bool
|
||||
|
||||
DisabledPostStartHooks sets.String
|
||||
|
||||
// Version will enable the /version endpoint if non-nil
|
||||
Version *version.Info
|
||||
// AuditBackend is where audit events are sent to.
|
||||
AuditBackend audit.Backend
|
||||
// AuditPolicyChecker makes the decision of whether and how to audit log a request.
|
||||
AuditPolicyChecker auditpolicy.Checker
|
||||
// ExternalAddress is the host name to use for external (public internet) facing URLs (e.g. Swagger)
|
||||
// Will default to a value based on secure serving info and available ipv4 IPs.
|
||||
ExternalAddress string
|
||||
|
||||
//===========================================================================
|
||||
// Fields you probably don't care about changing
|
||||
//===========================================================================
|
||||
|
||||
// BuildHandlerChainFunc allows you to build custom handler chains by decorating the apiHandler.
|
||||
BuildHandlerChainFunc func(apiHandler http.Handler, c *Config) (secure http.Handler)
|
||||
// HandlerChainWaitGroup allows you to wait for all chain handlers exit after the server shutdown.
|
||||
HandlerChainWaitGroup *utilwaitgroup.SafeWaitGroup
|
||||
// DiscoveryAddresses is used to build the IPs pass to discovery. If nil, the ExternalAddress is
|
||||
// always reported
|
||||
DiscoveryAddresses discovery.Addresses
|
||||
// The default set of healthz checks. There might be more added via AddHealthzChecks dynamically.
|
||||
HealthzChecks []healthz.HealthzChecker
|
||||
// LegacyAPIGroupPrefixes is used to set up URL parsing for authorization and for validating requests
|
||||
// to InstallLegacyAPIGroup. New API servers don't generally have legacy groups at all.
|
||||
LegacyAPIGroupPrefixes sets.String
|
||||
// RequestInfoResolver is used to assign attributes (used by admission and authorization) based on a request URL.
|
||||
// Use-cases that are like kubelets may need to customize this.
|
||||
RequestInfoResolver apirequest.RequestInfoResolver
|
||||
// Serializer is required and provides the interface for serializing and converting objects to and from the wire
|
||||
// The default (api.Codecs) usually works fine.
|
||||
Serializer runtime.NegotiatedSerializer
|
||||
// OpenAPIConfig will be used in generating OpenAPI spec. This is nil by default. Use DefaultOpenAPIConfig for "working" defaults.
|
||||
OpenAPIConfig *openapicommon.Config
|
||||
|
||||
// RESTOptionsGetter is used to construct RESTStorage types via the generic registry.
|
||||
RESTOptionsGetter genericregistry.RESTOptionsGetter
|
||||
|
||||
// If specified, all requests except those which match the LongRunningFunc predicate will timeout
|
||||
// after this duration.
|
||||
RequestTimeout time.Duration
|
||||
// If specified, long running requests such as watch will be allocated a random timeout between this value, and
|
||||
// twice this value. Note that it is up to the request handlers to ignore or honor this timeout. In seconds.
|
||||
MinRequestTimeout int
|
||||
// The limit on the total size increase all "copy" operations in a json
|
||||
// patch may cause.
|
||||
// This affects all places that applies json patch in the binary.
|
||||
JSONPatchMaxCopyBytes int64
|
||||
// The limit on the request body size that would be accepted and decoded in a write request.
|
||||
// 0 means no limit.
|
||||
MaxRequestBodyBytes int64
|
||||
// MaxRequestsInFlight is the maximum number of parallel non-long-running requests. Every further
|
||||
// request has to wait. Applies only to non-mutating requests.
|
||||
MaxRequestsInFlight int
|
||||
// MaxMutatingRequestsInFlight is the maximum number of parallel mutating requests. Every further
|
||||
// request has to wait.
|
||||
MaxMutatingRequestsInFlight int
|
||||
// Predicate which is true for paths of long-running http requests
|
||||
LongRunningFunc apirequest.LongRunningRequestCheck
|
||||
|
||||
// EnableAPIResponseCompression indicates whether API Responses should support compression
|
||||
// if the client requests it via Accept-Encoding
|
||||
EnableAPIResponseCompression bool
|
||||
|
||||
// MergedResourceConfig indicates which groupVersion enabled and its resources enabled/disabled.
|
||||
// This is composed of genericapiserver defaultAPIResourceConfig and those parsed from flags.
|
||||
// If not specify any in flags, then genericapiserver will only enable defaultAPIResourceConfig.
|
||||
MergedResourceConfig *serverstore.ResourceConfig
|
||||
|
||||
//===========================================================================
|
||||
// values below here are targets for removal
|
||||
//===========================================================================
|
||||
|
||||
// PublicAddress is the IP address where members of the cluster (kubelet,
|
||||
// kube-proxy, services, etc.) can reach the GenericAPIServer.
|
||||
// If nil or 0.0.0.0, the host's default interface will be used.
|
||||
PublicAddress net.IP
|
||||
}
|
||||
|
||||
type RecommendedConfig struct {
|
||||
Config
|
||||
|
||||
// SharedInformerFactory provides shared informers for Kubernetes resources. This value is set by
|
||||
// RecommendedOptions.CoreAPI.ApplyTo called by RecommendedOptions.ApplyTo. It uses an in-cluster client config
|
||||
// by default, or the kubeconfig given with kubeconfig command line flag.
|
||||
SharedInformerFactory informers.SharedInformerFactory
|
||||
|
||||
// ClientConfig holds the kubernetes client configuration.
|
||||
// This value is set by RecommendedOptions.CoreAPI.ApplyTo called by RecommendedOptions.ApplyTo.
|
||||
// By default in-cluster client config is used.
|
||||
ClientConfig *restclient.Config
|
||||
}
|
||||
|
||||
type SecureServingInfo struct {
|
||||
// Listener is the secure server network listener.
|
||||
Listener net.Listener
|
||||
|
||||
// Cert is the main server cert which is used if SNI does not match. Cert must be non-nil and is
|
||||
// allowed to be in SNICerts.
|
||||
Cert *tls.Certificate
|
||||
|
||||
// SNICerts are the TLS certificates by name used for SNI.
|
||||
SNICerts map[string]*tls.Certificate
|
||||
|
||||
// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
|
||||
ClientCA *x509.CertPool
|
||||
|
||||
// MinTLSVersion optionally overrides the minimum TLS version supported.
|
||||
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
|
||||
MinTLSVersion uint16
|
||||
|
||||
// CipherSuites optionally overrides the list of allowed cipher suites for the server.
|
||||
// Values are from tls package constants (https://golang.org/pkg/crypto/tls/#pkg-constants).
|
||||
CipherSuites []uint16
|
||||
|
||||
// HTTP2MaxStreamsPerConnection is the limit that the api server imposes on each client.
|
||||
// A value of zero means to use the default provided by golang's HTTP/2 support.
|
||||
HTTP2MaxStreamsPerConnection int
|
||||
}
|
||||
|
||||
type AuthenticationInfo struct {
|
||||
// APIAudiences is a list of identifier that the API identifies as. This is
|
||||
// used by some authenticators to validate audience bound credentials.
|
||||
APIAudiences authenticator.Audiences
|
||||
// Authenticator determines which subject is making the request
|
||||
Authenticator authenticator.Request
|
||||
// SupportsBasicAuth indicates that's at least one Authenticator supports basic auth
|
||||
// If this is true, a basic auth challenge is returned on authentication failure
|
||||
// TODO(roberthbailey): Remove once the server no longer supports http basic auth.
|
||||
SupportsBasicAuth bool
|
||||
}
|
||||
|
||||
type AuthorizationInfo struct {
|
||||
// Authorizer determines whether the subject is allowed to make the request based only
|
||||
// on the RequestURI
|
||||
Authorizer authorizer.Authorizer
|
||||
}
|
||||
|
||||
// NewConfig returns a Config struct with the default values
|
||||
func NewConfig(codecs serializer.CodecFactory) *Config {
|
||||
return &Config{
|
||||
Serializer: codecs,
|
||||
BuildHandlerChainFunc: DefaultBuildHandlerChain,
|
||||
HandlerChainWaitGroup: new(utilwaitgroup.SafeWaitGroup),
|
||||
LegacyAPIGroupPrefixes: sets.NewString(DefaultLegacyAPIPrefix),
|
||||
DisabledPostStartHooks: sets.NewString(),
|
||||
HealthzChecks: []healthz.HealthzChecker{healthz.PingHealthz, healthz.LogHealthz},
|
||||
EnableIndex: true,
|
||||
EnableDiscovery: true,
|
||||
EnableProfiling: true,
|
||||
EnableMetrics: true,
|
||||
MaxRequestsInFlight: 400,
|
||||
MaxMutatingRequestsInFlight: 200,
|
||||
RequestTimeout: time.Duration(60) * time.Second,
|
||||
MinRequestTimeout: 1800,
|
||||
// 10MB is the recommended maximum client request size in bytes
|
||||
// the etcd server should accept. See
|
||||
// https://github.com/etcd-io/etcd/blob/release-3.3/etcdserver/server.go#L90.
|
||||
// A request body might be encoded in json, and is converted to
|
||||
// proto when persisted in etcd. Assuming the upper bound of
|
||||
// the size ratio is 10:1, we set 100MB as the largest size
|
||||
// increase the "copy" operations in a json patch may cause.
|
||||
JSONPatchMaxCopyBytes: int64(100 * 1024 * 1024),
|
||||
// 10MB is the recommended maximum client request size in bytes
|
||||
// the etcd server should accept. See
|
||||
// https://github.com/etcd-io/etcd/blob/release-3.3/etcdserver/server.go#L90.
|
||||
// A request body might be encoded in json, and is converted to
|
||||
// proto when persisted in etcd. Assuming the upper bound of
|
||||
// the size ratio is 10:1, we set 100MB as the largest request
|
||||
// body size to be accepted and decoded in a write request.
|
||||
MaxRequestBodyBytes: int64(100 * 1024 * 1024),
|
||||
EnableAPIResponseCompression: utilfeature.DefaultFeatureGate.Enabled(features.APIResponseCompression),
|
||||
|
||||
// Default to treating watch as a long-running operation
|
||||
// Generic API servers have no inherent long-running subresources
|
||||
LongRunningFunc: genericfilters.BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString()),
|
||||
}
|
||||
}
|
||||
|
||||
// NewRecommendedConfig returns a RecommendedConfig struct with the default values
|
||||
func NewRecommendedConfig(codecs serializer.CodecFactory) *RecommendedConfig {
|
||||
return &RecommendedConfig{
|
||||
Config: *NewConfig(codecs),
|
||||
}
|
||||
}
|
||||
|
||||
func DefaultOpenAPIConfig(getDefinitions openapicommon.GetOpenAPIDefinitions, defNamer *apiopenapi.DefinitionNamer) *openapicommon.Config {
|
||||
return &openapicommon.Config{
|
||||
ProtocolList: []string{"https"},
|
||||
IgnorePrefixes: []string{},
|
||||
Info: &spec.Info{
|
||||
InfoProps: spec.InfoProps{
|
||||
Title: "Generic API Server",
|
||||
},
|
||||
},
|
||||
DefaultResponse: &spec.Response{
|
||||
ResponseProps: spec.ResponseProps{
|
||||
Description: "Default Response.",
|
||||
},
|
||||
},
|
||||
GetOperationIDAndTags: apiopenapi.GetOperationIDAndTags,
|
||||
GetDefinitionName: defNamer.GetDefinitionName,
|
||||
GetDefinitions: getDefinitions,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AuthenticationInfo) ApplyClientCert(clientCAFile string, servingInfo *SecureServingInfo) error {
|
||||
if servingInfo != nil {
|
||||
if len(clientCAFile) > 0 {
|
||||
clientCAs, err := certutil.CertsFromFile(clientCAFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
if servingInfo.ClientCA == nil {
|
||||
servingInfo.ClientCA = x509.NewCertPool()
|
||||
}
|
||||
for _, cert := range clientCAs {
|
||||
servingInfo.ClientCA.AddCert(cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type completedConfig struct {
|
||||
*Config
|
||||
|
||||
//===========================================================================
|
||||
// values below here are filled in during completion
|
||||
//===========================================================================
|
||||
|
||||
// SharedInformerFactory provides shared informers for resources
|
||||
SharedInformerFactory informers.SharedInformerFactory
|
||||
}
|
||||
|
||||
type CompletedConfig struct {
|
||||
// Embed a private pointer that cannot be instantiated outside of this package.
|
||||
*completedConfig
|
||||
}
|
||||
|
||||
// Complete fills in any fields not set that are required to have valid data and can be derived
|
||||
// from other fields. If you're going to `ApplyOptions`, do that first. It's mutating the receiver.
|
||||
func (c *Config) Complete(informers informers.SharedInformerFactory) CompletedConfig {
|
||||
if len(c.ExternalAddress) == 0 && c.PublicAddress != nil {
|
||||
c.ExternalAddress = c.PublicAddress.String()
|
||||
}
|
||||
|
||||
// if there is no port, and we listen on one securely, use that one
|
||||
if _, _, err := net.SplitHostPort(c.ExternalAddress); err != nil {
|
||||
if c.SecureServing == nil {
|
||||
klog.Fatalf("cannot derive external address port without listening on a secure port.")
|
||||
}
|
||||
_, port, err := c.SecureServing.HostPort()
|
||||
if err != nil {
|
||||
klog.Fatalf("cannot derive external address from the secure port: %v", err)
|
||||
}
|
||||
c.ExternalAddress = net.JoinHostPort(c.ExternalAddress, strconv.Itoa(port))
|
||||
}
|
||||
|
||||
if c.OpenAPIConfig != nil {
|
||||
if c.OpenAPIConfig.SecurityDefinitions != nil {
|
||||
// Setup OpenAPI security: all APIs will have the same authentication for now.
|
||||
c.OpenAPIConfig.DefaultSecurity = []map[string][]string{}
|
||||
keys := []string{}
|
||||
for k := range *c.OpenAPIConfig.SecurityDefinitions {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
c.OpenAPIConfig.DefaultSecurity = append(c.OpenAPIConfig.DefaultSecurity, map[string][]string{k: {}})
|
||||
}
|
||||
if c.OpenAPIConfig.CommonResponses == nil {
|
||||
c.OpenAPIConfig.CommonResponses = map[int]spec.Response{}
|
||||
}
|
||||
if _, exists := c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized]; !exists {
|
||||
c.OpenAPIConfig.CommonResponses[http.StatusUnauthorized] = spec.Response{
|
||||
ResponseProps: spec.ResponseProps{
|
||||
Description: "Unauthorized",
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we populate info, and info.version, if not manually set
|
||||
if c.OpenAPIConfig.Info == nil {
|
||||
c.OpenAPIConfig.Info = &spec.Info{}
|
||||
}
|
||||
if c.OpenAPIConfig.Info.Version == "" {
|
||||
if c.Version != nil {
|
||||
c.OpenAPIConfig.Info.Version = strings.Split(c.Version.String(), "-")[0]
|
||||
} else {
|
||||
c.OpenAPIConfig.Info.Version = "unversioned"
|
||||
}
|
||||
}
|
||||
}
|
||||
if c.DiscoveryAddresses == nil {
|
||||
c.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: c.ExternalAddress}
|
||||
}
|
||||
|
||||
AuthorizeClientBearerToken(c.LoopbackClientConfig, &c.Authentication, &c.Authorization)
|
||||
|
||||
if c.RequestInfoResolver == nil {
|
||||
c.RequestInfoResolver = NewRequestInfoResolver(c)
|
||||
}
|
||||
|
||||
return CompletedConfig{&completedConfig{c, informers}}
|
||||
}
|
||||
|
||||
// Complete fills in any fields not set that are required to have valid data and can be derived
|
||||
// from other fields. If you're going to `ApplyOptions`, do that first. It's mutating the receiver.
|
||||
func (c *RecommendedConfig) Complete() CompletedConfig {
|
||||
return c.Config.Complete(c.SharedInformerFactory)
|
||||
}
|
||||
|
||||
// New creates a new server which logically combines the handling chain with the passed server.
|
||||
// name is used to differentiate for logging. The handler chain in particular can be difficult as it starts delgating.
|
||||
// delegationTarget may not be nil.
|
||||
func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*GenericAPIServer, error) {
|
||||
if c.Serializer == nil {
|
||||
return nil, fmt.Errorf("Genericapiserver.New() called with config.Serializer == nil")
|
||||
}
|
||||
if c.LoopbackClientConfig == nil {
|
||||
return nil, fmt.Errorf("Genericapiserver.New() called with config.LoopbackClientConfig == nil")
|
||||
}
|
||||
|
||||
handlerChainBuilder := func(handler http.Handler) http.Handler {
|
||||
return c.BuildHandlerChainFunc(handler, c.Config)
|
||||
}
|
||||
apiServerHandler := NewAPIServerHandler(name, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
|
||||
|
||||
s := &GenericAPIServer{
|
||||
discoveryAddresses: c.DiscoveryAddresses,
|
||||
LoopbackClientConfig: c.LoopbackClientConfig,
|
||||
legacyAPIGroupPrefixes: c.LegacyAPIGroupPrefixes,
|
||||
admissionControl: c.AdmissionControl,
|
||||
Serializer: c.Serializer,
|
||||
AuditBackend: c.AuditBackend,
|
||||
Authorizer: c.Authorization.Authorizer,
|
||||
delegationTarget: delegationTarget,
|
||||
HandlerChainWaitGroup: c.HandlerChainWaitGroup,
|
||||
|
||||
minRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,
|
||||
ShutdownTimeout: c.RequestTimeout,
|
||||
|
||||
SecureServingInfo: c.SecureServing,
|
||||
ExternalAddress: c.ExternalAddress,
|
||||
|
||||
Handler: apiServerHandler,
|
||||
|
||||
listedPathProvider: apiServerHandler,
|
||||
|
||||
openAPIConfig: c.OpenAPIConfig,
|
||||
|
||||
postStartHooks: map[string]postStartHookEntry{},
|
||||
preShutdownHooks: map[string]preShutdownHookEntry{},
|
||||
disabledPostStartHooks: c.DisabledPostStartHooks,
|
||||
|
||||
healthzChecks: c.HealthzChecks,
|
||||
|
||||
DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer),
|
||||
|
||||
enableAPIResponseCompression: c.EnableAPIResponseCompression,
|
||||
maxRequestBodyBytes: c.MaxRequestBodyBytes,
|
||||
}
|
||||
|
||||
for {
|
||||
if c.JSONPatchMaxCopyBytes <= 0 {
|
||||
break
|
||||
}
|
||||
existing := atomic.LoadInt64(&jsonpatch.AccumulatedCopySizeLimit)
|
||||
if existing > 0 && existing < c.JSONPatchMaxCopyBytes {
|
||||
break
|
||||
}
|
||||
if atomic.CompareAndSwapInt64(&jsonpatch.AccumulatedCopySizeLimit, existing, c.JSONPatchMaxCopyBytes) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
for k, v := range delegationTarget.PostStartHooks() {
|
||||
s.postStartHooks[k] = v
|
||||
}
|
||||
|
||||
for k, v := range delegationTarget.PreShutdownHooks() {
|
||||
s.preShutdownHooks[k] = v
|
||||
}
|
||||
|
||||
genericApiServerHookName := "generic-apiserver-start-informers"
|
||||
if c.SharedInformerFactory != nil && !s.isPostStartHookRegistered(genericApiServerHookName) {
|
||||
err := s.AddPostStartHook(genericApiServerHookName, func(context PostStartHookContext) error {
|
||||
c.SharedInformerFactory.Start(context.StopCh)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, delegateCheck := range delegationTarget.HealthzChecks() {
|
||||
skip := false
|
||||
for _, existingCheck := range c.HealthzChecks {
|
||||
if existingCheck.Name() == delegateCheck.Name() {
|
||||
skip = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if skip {
|
||||
continue
|
||||
}
|
||||
|
||||
s.healthzChecks = append(s.healthzChecks, delegateCheck)
|
||||
}
|
||||
|
||||
s.listedPathProvider = routes.ListedPathProviders{s.listedPathProvider, delegationTarget}
|
||||
|
||||
installAPI(s, c.Config)
|
||||
|
||||
// use the UnprotectedHandler from the delegation target to ensure that we don't attempt to double authenticator, authorize,
|
||||
// or some other part of the filter chain in delegation cases.
|
||||
if delegationTarget.UnprotectedHandler() == nil && c.EnableIndex {
|
||||
s.Handler.NonGoRestfulMux.NotFoundHandler(routes.IndexLister{
|
||||
StatusCode: http.StatusNotFound,
|
||||
PathProvider: s.listedPathProvider,
|
||||
})
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
|
||||
handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)
|
||||
handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.LongRunningFunc)
|
||||
handler = genericapifilters.WithImpersonation(handler, c.Authorization.Authorizer, c.Serializer)
|
||||
handler = genericapifilters.WithAudit(handler, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
||||
failedHandler := genericapifilters.Unauthorized(c.Serializer, c.Authentication.SupportsBasicAuth)
|
||||
failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.AuditBackend, c.AuditPolicyChecker)
|
||||
handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences)
|
||||
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
||||
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.LongRunningFunc, c.RequestTimeout)
|
||||
handler = genericfilters.WithWaitGroup(handler, c.LongRunningFunc, c.HandlerChainWaitGroup)
|
||||
handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
|
||||
handler = genericfilters.WithPanicRecovery(handler)
|
||||
return handler
|
||||
}
|
||||
|
||||
func installAPI(s *GenericAPIServer, c *Config) {
|
||||
if c.EnableIndex {
|
||||
routes.Index{}.Install(s.listedPathProvider, s.Handler.NonGoRestfulMux)
|
||||
}
|
||||
if c.EnableProfiling {
|
||||
routes.Profiling{}.Install(s.Handler.NonGoRestfulMux)
|
||||
if c.EnableContentionProfiling {
|
||||
goruntime.SetBlockProfileRate(1)
|
||||
}
|
||||
// so far, only logging related endpoints are considered valid to add for these debug flags.
|
||||
routes.DebugFlags{}.Install(s.Handler.NonGoRestfulMux, "v", routes.StringFlagPutHandler(logs.GlogSetter))
|
||||
}
|
||||
if c.EnableMetrics {
|
||||
if c.EnableProfiling {
|
||||
routes.MetricsWithReset{}.Install(s.Handler.NonGoRestfulMux)
|
||||
} else {
|
||||
routes.DefaultMetrics{}.Install(s.Handler.NonGoRestfulMux)
|
||||
}
|
||||
}
|
||||
|
||||
routes.Version{Version: c.Version}.Install(s.Handler.GoRestfulContainer)
|
||||
|
||||
if c.EnableDiscovery {
|
||||
s.Handler.GoRestfulContainer.Add(s.DiscoveryGroupManager.WebService())
|
||||
}
|
||||
}
|
||||
|
||||
func NewRequestInfoResolver(c *Config) *apirequest.RequestInfoFactory {
|
||||
apiPrefixes := sets.NewString(strings.Trim(APIGroupPrefix, "/")) // all possible API prefixes
|
||||
legacyAPIPrefixes := sets.String{} // APIPrefixes that won't have groups (legacy)
|
||||
for legacyAPIPrefix := range c.LegacyAPIGroupPrefixes {
|
||||
apiPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/"))
|
||||
legacyAPIPrefixes.Insert(strings.Trim(legacyAPIPrefix, "/"))
|
||||
}
|
||||
|
||||
return &apirequest.RequestInfoFactory{
|
||||
APIPrefixes: apiPrefixes,
|
||||
GrouplessAPIPrefixes: legacyAPIPrefixes,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SecureServingInfo) HostPort() (string, int, error) {
|
||||
if s == nil || s.Listener == nil {
|
||||
return "", 0, fmt.Errorf("no listener found")
|
||||
}
|
||||
addr := s.Listener.Addr().String()
|
||||
host, portStr, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("failed to get port from listener address %q: %v", addr, err)
|
||||
}
|
||||
port, err := strconv.Atoi(portStr)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("invalid non-numeric port %q", portStr)
|
||||
}
|
||||
return host, port, nil
|
||||
}
|
||||
|
||||
// AuthorizeClientBearerToken wraps the authenticator and authorizer in loopback authentication logic
|
||||
// if the loopback client config is specified AND it has a bearer token.
|
||||
func AuthorizeClientBearerToken(loopback *restclient.Config, authn *AuthenticationInfo, authz *AuthorizationInfo) {
|
||||
if loopback == nil || authn == nil || authz == nil || authn.Authenticator == nil && authz.Authorizer == nil || len(loopback.BearerToken) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
privilegedLoopbackToken := loopback.BearerToken
|
||||
var uid = uuid.NewRandom().String()
|
||||
tokens := make(map[string]*user.DefaultInfo)
|
||||
tokens[privilegedLoopbackToken] = &user.DefaultInfo{
|
||||
Name: user.APIServerUser,
|
||||
UID: uid,
|
||||
Groups: []string{user.SystemPrivilegedGroup},
|
||||
}
|
||||
|
||||
tokenAuthenticator := authenticatorfactory.NewFromTokens(tokens)
|
||||
authn.Authenticator = authenticatorunion.New(tokenAuthenticator, authn.Authenticator)
|
||||
|
||||
tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
|
||||
authz.Authorizer = authorizerunion.New(tokenAuthorizer, authz.Authorizer)
|
||||
}
|
||||
95
vendor/k8s.io/apiserver/pkg/server/config_selfclient.go
generated
vendored
95
vendor/k8s.io/apiserver/pkg/server/config_selfclient.go
generated
vendored
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
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 server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// LoopbackClientServerNameOverride is passed to the apiserver from the loopback client in order to
|
||||
// select the loopback certificate via SNI if TLS is used.
|
||||
const LoopbackClientServerNameOverride = "apiserver-loopback-client"
|
||||
|
||||
func (s *SecureServingInfo) NewClientConfig(caCert []byte) (*restclient.Config, error) {
|
||||
if s == nil || (s.Cert == nil && len(s.SNICerts) == 0) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
host, port, err := LoopbackHostPort(s.Listener.Addr().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &restclient.Config{
|
||||
// Increase QPS limits. The client is currently passed to all admission plugins,
|
||||
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
|
||||
// for more details. Once #22422 is fixed, we may want to remove it.
|
||||
QPS: 50,
|
||||
Burst: 100,
|
||||
Host: "https://" + net.JoinHostPort(host, port),
|
||||
// override the ServerName to select our loopback certificate via SNI. This name is also
|
||||
// used by the client to compare the returns server certificate against.
|
||||
TLSClientConfig: restclient.TLSClientConfig{
|
||||
CAData: caCert,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SecureServingInfo) NewLoopbackClientConfig(token string, loopbackCert []byte) (*restclient.Config, error) {
|
||||
c, err := s.NewClientConfig(loopbackCert)
|
||||
if err != nil || c == nil {
|
||||
return c, err
|
||||
}
|
||||
|
||||
c.BearerToken = token
|
||||
c.TLSClientConfig.ServerName = LoopbackClientServerNameOverride
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// LoopbackHostPort returns the host and port loopback REST clients should use
|
||||
// to contact the server.
|
||||
func LoopbackHostPort(bindAddress string) (string, string, error) {
|
||||
host, port, err := net.SplitHostPort(bindAddress)
|
||||
if err != nil {
|
||||
// should never happen
|
||||
return "", "", fmt.Errorf("invalid server bind address: %q", bindAddress)
|
||||
}
|
||||
|
||||
isIPv6 := net.ParseIP(host).To4() == nil
|
||||
|
||||
// Value is expected to be an IP or DNS name, not "0.0.0.0".
|
||||
if host == "0.0.0.0" || host == "::" {
|
||||
host = "localhost"
|
||||
// Get ip of local interface, but fall back to "localhost".
|
||||
// Note that "localhost" is resolved with the external nameserver first with Go's stdlib.
|
||||
// So if localhost.<yoursearchdomain> resolves, we don't get a 127.0.0.1 as expected.
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err == nil {
|
||||
for _, address := range addrs {
|
||||
if ipnet, ok := address.(*net.IPNet); ok && ipnet.IP.IsLoopback() && isIPv6 == (ipnet.IP.To4() == nil) {
|
||||
host = ipnet.IP.String()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return host, port, nil
|
||||
}
|
||||
92
vendor/k8s.io/apiserver/pkg/server/deprecated_insecure_serving.go
generated
vendored
92
vendor/k8s.io/apiserver/pkg/server/deprecated_insecure_serving.go
generated
vendored
|
|
@ -1,92 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 server
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apiserver/pkg/authentication/authenticator"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// DeprecatedInsecureServingInfo is the main context object for the insecure http server.
|
||||
type DeprecatedInsecureServingInfo struct {
|
||||
// Listener is the secure server network listener.
|
||||
Listener net.Listener
|
||||
// optional server name for log messages
|
||||
Name string
|
||||
}
|
||||
|
||||
// Serve starts an insecure http server with the given handler. It fails only if
|
||||
// the initial listen call fails. It does not block.
|
||||
func (s *DeprecatedInsecureServingInfo) Serve(handler http.Handler, shutdownTimeout time.Duration, stopCh <-chan struct{}) error {
|
||||
insecureServer := &http.Server{
|
||||
Addr: s.Listener.Addr().String(),
|
||||
Handler: handler,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
}
|
||||
|
||||
if len(s.Name) > 0 {
|
||||
klog.Infof("Serving %s insecurely on %s", s.Name, s.Listener.Addr())
|
||||
} else {
|
||||
klog.Infof("Serving insecurely on %s", s.Listener.Addr())
|
||||
}
|
||||
_, err := RunServer(insecureServer, s.Listener, shutdownTimeout, stopCh)
|
||||
// NOTE: we do not handle stoppedCh returned by RunServer for graceful termination here
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *DeprecatedInsecureServingInfo) NewLoopbackClientConfig() (*rest.Config, error) {
|
||||
if s == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
host, port, err := LoopbackHostPort(s.Listener.Addr().String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &rest.Config{
|
||||
Host: "http://" + net.JoinHostPort(host, port),
|
||||
// Increase QPS limits. The client is currently passed to all admission plugins,
|
||||
// and those can be throttled in case of higher load on apiserver - see #22340 and #22422
|
||||
// for more details. Once #22422 is fixed, we may want to remove it.
|
||||
QPS: 50,
|
||||
Burst: 100,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// InsecureSuperuser implements authenticator.Request to always return a superuser.
|
||||
// This is functionally equivalent to skipping authentication and authorization,
|
||||
// but allows apiserver code to stop special-casing a nil user to skip authorization checks.
|
||||
type InsecureSuperuser struct{}
|
||||
|
||||
func (InsecureSuperuser) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
|
||||
auds, _ := authenticator.AudiencesFrom(req.Context())
|
||||
return &authenticator.Response{
|
||||
User: &user.DefaultInfo{
|
||||
Name: "system:unsecured",
|
||||
Groups: []string{user.SystemPrivilegedGroup, user.AllAuthenticated},
|
||||
},
|
||||
Audiences: auds,
|
||||
}, true, nil
|
||||
}
|
||||
18
vendor/k8s.io/apiserver/pkg/server/doc.go
generated
vendored
18
vendor/k8s.io/apiserver/pkg/server/doc.go
generated
vendored
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
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 server contains the plumbing to create kubernetes-like API server command.
|
||||
package server // import "k8s.io/apiserver/pkg/server"
|
||||
534
vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
generated
vendored
534
vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
generated
vendored
|
|
@ -1,534 +0,0 @@
|
|||
/*
|
||||
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 server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
gpath "path"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
systemd "github.com/coreos/go-systemd/daemon"
|
||||
"github.com/go-openapi/spec"
|
||||
"k8s.io/klog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilwaitgroup "k8s.io/apimachinery/pkg/util/waitgroup"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
genericapi "k8s.io/apiserver/pkg/endpoints"
|
||||
"k8s.io/apiserver/pkg/endpoints/discovery"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
"k8s.io/apiserver/pkg/server/routes"
|
||||
utilopenapi "k8s.io/apiserver/pkg/util/openapi"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
openapibuilder "k8s.io/kube-openapi/pkg/builder"
|
||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/handler"
|
||||
openapiutil "k8s.io/kube-openapi/pkg/util"
|
||||
openapiproto "k8s.io/kube-openapi/pkg/util/proto"
|
||||
)
|
||||
|
||||
// Info about an API group.
|
||||
type APIGroupInfo struct {
|
||||
PrioritizedVersions []schema.GroupVersion
|
||||
// Info about the resources in this group. It's a map from version to resource to the storage.
|
||||
VersionedResourcesStorageMap map[string]map[string]rest.Storage
|
||||
// OptionsExternalVersion controls the APIVersion used for common objects in the
|
||||
// schema like api.Status, api.DeleteOptions, and metav1.ListOptions. Other implementors may
|
||||
// define a version "v1beta1" but want to use the Kubernetes "v1" internal objects.
|
||||
// If nil, defaults to groupMeta.GroupVersion.
|
||||
// TODO: Remove this when https://github.com/kubernetes/kubernetes/issues/19018 is fixed.
|
||||
OptionsExternalVersion *schema.GroupVersion
|
||||
// MetaGroupVersion defaults to "meta.k8s.io/v1" and is the scheme group version used to decode
|
||||
// common API implementations like ListOptions. Future changes will allow this to vary by group
|
||||
// version (for when the inevitable meta/v2 group emerges).
|
||||
MetaGroupVersion *schema.GroupVersion
|
||||
|
||||
// Scheme includes all of the types used by this group and how to convert between them (or
|
||||
// to convert objects from outside of this group that are accepted in this API).
|
||||
// TODO: replace with interfaces
|
||||
Scheme *runtime.Scheme
|
||||
// NegotiatedSerializer controls how this group encodes and decodes data
|
||||
NegotiatedSerializer runtime.NegotiatedSerializer
|
||||
// ParameterCodec performs conversions for query parameters passed to API calls
|
||||
ParameterCodec runtime.ParameterCodec
|
||||
}
|
||||
|
||||
// GenericAPIServer contains state for a Kubernetes cluster api server.
|
||||
type GenericAPIServer struct {
|
||||
// discoveryAddresses is used to build cluster IPs for discovery.
|
||||
discoveryAddresses discovery.Addresses
|
||||
|
||||
// LoopbackClientConfig is a config for a privileged loopback connection to the API server
|
||||
LoopbackClientConfig *restclient.Config
|
||||
|
||||
// minRequestTimeout is how short the request timeout can be. This is used to build the RESTHandler
|
||||
minRequestTimeout time.Duration
|
||||
|
||||
// ShutdownTimeout is the timeout used for server shutdown. This specifies the timeout before server
|
||||
// gracefully shutdown returns.
|
||||
ShutdownTimeout time.Duration
|
||||
|
||||
// legacyAPIGroupPrefixes is used to set up URL parsing for authorization and for validating requests
|
||||
// to InstallLegacyAPIGroup
|
||||
legacyAPIGroupPrefixes sets.String
|
||||
|
||||
// admissionControl is used to build the RESTStorage that backs an API Group.
|
||||
admissionControl admission.Interface
|
||||
|
||||
// SecureServingInfo holds configuration of the TLS server.
|
||||
SecureServingInfo *SecureServingInfo
|
||||
|
||||
// ExternalAddress is the address (hostname or IP and port) that should be used in
|
||||
// external (public internet) URLs for this GenericAPIServer.
|
||||
ExternalAddress string
|
||||
|
||||
// Serializer controls how common API objects not in a group/version prefix are serialized for this server.
|
||||
// Individual APIGroups may define their own serializers.
|
||||
Serializer runtime.NegotiatedSerializer
|
||||
|
||||
// "Outputs"
|
||||
// Handler holds the handlers being used by this API server
|
||||
Handler *APIServerHandler
|
||||
|
||||
// listedPathProvider is a lister which provides the set of paths to show at /
|
||||
listedPathProvider routes.ListedPathProvider
|
||||
|
||||
// DiscoveryGroupManager serves /apis
|
||||
DiscoveryGroupManager discovery.GroupManager
|
||||
|
||||
// Enable swagger and/or OpenAPI if these configs are non-nil.
|
||||
openAPIConfig *openapicommon.Config
|
||||
|
||||
// OpenAPIVersionedService controls the /openapi/v2 endpoint, and can be used to update the served spec.
|
||||
// It is set during PrepareRun.
|
||||
OpenAPIVersionedService *handler.OpenAPIService
|
||||
|
||||
// StaticOpenAPISpec is the spec derived from the restful container endpoints.
|
||||
// It is set during PrepareRun.
|
||||
StaticOpenAPISpec *spec.Swagger
|
||||
|
||||
// PostStartHooks are each called after the server has started listening, in a separate go func for each
|
||||
// with no guarantee of ordering between them. The map key is a name used for error reporting.
|
||||
// It may kill the process with a panic if it wishes to by returning an error.
|
||||
postStartHookLock sync.Mutex
|
||||
postStartHooks map[string]postStartHookEntry
|
||||
postStartHooksCalled bool
|
||||
disabledPostStartHooks sets.String
|
||||
|
||||
preShutdownHookLock sync.Mutex
|
||||
preShutdownHooks map[string]preShutdownHookEntry
|
||||
preShutdownHooksCalled bool
|
||||
|
||||
// healthz checks
|
||||
healthzLock sync.Mutex
|
||||
healthzChecks []healthz.HealthzChecker
|
||||
healthzCreated bool
|
||||
|
||||
// auditing. The backend is started after the server starts listening.
|
||||
AuditBackend audit.Backend
|
||||
|
||||
// Authorizer determines whether a user is allowed to make a certain request. The Handler does a preliminary
|
||||
// authorization check using the request URI but it may be necessary to make additional checks, such as in
|
||||
// the create-on-update case
|
||||
Authorizer authorizer.Authorizer
|
||||
|
||||
// enableAPIResponseCompression indicates whether API Responses should support compression
|
||||
// if the client requests it via Accept-Encoding
|
||||
enableAPIResponseCompression bool
|
||||
|
||||
// delegationTarget is the next delegate in the chain. This is never nil.
|
||||
delegationTarget DelegationTarget
|
||||
|
||||
// HandlerChainWaitGroup allows you to wait for all chain handlers finish after the server shutdown.
|
||||
HandlerChainWaitGroup *utilwaitgroup.SafeWaitGroup
|
||||
|
||||
// The limit on the request body size that would be accepted and decoded in a write request.
|
||||
// 0 means no limit.
|
||||
maxRequestBodyBytes int64
|
||||
}
|
||||
|
||||
// DelegationTarget is an interface which allows for composition of API servers with top level handling that works
|
||||
// as expected.
|
||||
type DelegationTarget interface {
|
||||
// UnprotectedHandler returns a handler that is NOT protected by a normal chain
|
||||
UnprotectedHandler() http.Handler
|
||||
|
||||
// PostStartHooks returns the post-start hooks that need to be combined
|
||||
PostStartHooks() map[string]postStartHookEntry
|
||||
|
||||
// PreShutdownHooks returns the pre-stop hooks that need to be combined
|
||||
PreShutdownHooks() map[string]preShutdownHookEntry
|
||||
|
||||
// HealthzChecks returns the healthz checks that need to be combined
|
||||
HealthzChecks() []healthz.HealthzChecker
|
||||
|
||||
// ListedPaths returns the paths for supporting an index
|
||||
ListedPaths() []string
|
||||
|
||||
// NextDelegate returns the next delegationTarget in the chain of delegations
|
||||
NextDelegate() DelegationTarget
|
||||
}
|
||||
|
||||
func (s *GenericAPIServer) UnprotectedHandler() http.Handler {
|
||||
// when we delegate, we need the server we're delegating to choose whether or not to use gorestful
|
||||
return s.Handler.Director
|
||||
}
|
||||
func (s *GenericAPIServer) PostStartHooks() map[string]postStartHookEntry {
|
||||
return s.postStartHooks
|
||||
}
|
||||
func (s *GenericAPIServer) PreShutdownHooks() map[string]preShutdownHookEntry {
|
||||
return s.preShutdownHooks
|
||||
}
|
||||
func (s *GenericAPIServer) HealthzChecks() []healthz.HealthzChecker {
|
||||
return s.healthzChecks
|
||||
}
|
||||
func (s *GenericAPIServer) ListedPaths() []string {
|
||||
return s.listedPathProvider.ListedPaths()
|
||||
}
|
||||
|
||||
func (s *GenericAPIServer) NextDelegate() DelegationTarget {
|
||||
return s.delegationTarget
|
||||
}
|
||||
|
||||
type emptyDelegate struct {
|
||||
}
|
||||
|
||||
func NewEmptyDelegate() DelegationTarget {
|
||||
return emptyDelegate{}
|
||||
}
|
||||
|
||||
func (s emptyDelegate) UnprotectedHandler() http.Handler {
|
||||
return nil
|
||||
}
|
||||
func (s emptyDelegate) PostStartHooks() map[string]postStartHookEntry {
|
||||
return map[string]postStartHookEntry{}
|
||||
}
|
||||
func (s emptyDelegate) PreShutdownHooks() map[string]preShutdownHookEntry {
|
||||
return map[string]preShutdownHookEntry{}
|
||||
}
|
||||
func (s emptyDelegate) HealthzChecks() []healthz.HealthzChecker {
|
||||
return []healthz.HealthzChecker{}
|
||||
}
|
||||
func (s emptyDelegate) ListedPaths() []string {
|
||||
return []string{}
|
||||
}
|
||||
func (s emptyDelegate) NextDelegate() DelegationTarget {
|
||||
return nil
|
||||
}
|
||||
|
||||
// preparedGenericAPIServer is a private wrapper that enforces a call of PrepareRun() before Run can be invoked.
|
||||
type preparedGenericAPIServer struct {
|
||||
*GenericAPIServer
|
||||
}
|
||||
|
||||
// PrepareRun does post API installation setup steps.
|
||||
func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
|
||||
if s.openAPIConfig != nil {
|
||||
s.OpenAPIVersionedService, s.StaticOpenAPISpec = routes.OpenAPI{
|
||||
Config: s.openAPIConfig,
|
||||
}.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux)
|
||||
}
|
||||
|
||||
s.installHealthz()
|
||||
|
||||
// Register audit backend preShutdownHook.
|
||||
if s.AuditBackend != nil {
|
||||
s.AddPreShutdownHook("audit-backend", func() error {
|
||||
s.AuditBackend.Shutdown()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
return preparedGenericAPIServer{s}
|
||||
}
|
||||
|
||||
// Run spawns the secure http server. It only returns if stopCh is closed
|
||||
// or the secure port cannot be listened on initially.
|
||||
func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) error {
|
||||
err := s.NonBlockingRun(stopCh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
<-stopCh
|
||||
|
||||
err = s.RunPreShutdownHooks()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for all requests to finish, which are bounded by the RequestTimeout variable.
|
||||
s.HandlerChainWaitGroup.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NonBlockingRun spawns the secure http server. An error is
|
||||
// returned if the secure port cannot be listened on.
|
||||
func (s preparedGenericAPIServer) NonBlockingRun(stopCh <-chan struct{}) error {
|
||||
// Use an stop channel to allow graceful shutdown without dropping audit events
|
||||
// after http server shutdown.
|
||||
auditStopCh := make(chan struct{})
|
||||
|
||||
// Start the audit backend before any request comes in. This means we must call Backend.Run
|
||||
// before http server start serving. Otherwise the Backend.ProcessEvents call might block.
|
||||
if s.AuditBackend != nil {
|
||||
if err := s.AuditBackend.Run(auditStopCh); err != nil {
|
||||
return fmt.Errorf("failed to run the audit backend: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Use an internal stop channel to allow cleanup of the listeners on error.
|
||||
internalStopCh := make(chan struct{})
|
||||
var stoppedCh <-chan struct{}
|
||||
if s.SecureServingInfo != nil && s.Handler != nil {
|
||||
var err error
|
||||
stoppedCh, err = s.SecureServingInfo.Serve(s.Handler, s.ShutdownTimeout, internalStopCh)
|
||||
if err != nil {
|
||||
close(internalStopCh)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Now that listener have bound successfully, it is the
|
||||
// responsibility of the caller to close the provided channel to
|
||||
// ensure cleanup.
|
||||
go func() {
|
||||
<-stopCh
|
||||
close(internalStopCh)
|
||||
if stoppedCh != nil {
|
||||
<-stoppedCh
|
||||
}
|
||||
s.HandlerChainWaitGroup.Wait()
|
||||
close(auditStopCh)
|
||||
}()
|
||||
|
||||
s.RunPostStartHooks(stopCh)
|
||||
|
||||
if _, err := systemd.SdNotify(true, "READY=1\n"); err != nil {
|
||||
klog.Errorf("Unable to send systemd daemon successful start message: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// installAPIResources is a private method for installing the REST storage backing each api groupversionresource
|
||||
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo, openAPIModels openapiproto.Models) error {
|
||||
for _, groupVersion := range apiGroupInfo.PrioritizedVersions {
|
||||
if len(apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version]) == 0 {
|
||||
klog.Warningf("Skipping API %v because it has no resources.", groupVersion)
|
||||
continue
|
||||
}
|
||||
|
||||
apiGroupVersion := s.getAPIGroupVersion(apiGroupInfo, groupVersion, apiPrefix)
|
||||
if apiGroupInfo.OptionsExternalVersion != nil {
|
||||
apiGroupVersion.OptionsExternalVersion = apiGroupInfo.OptionsExternalVersion
|
||||
}
|
||||
apiGroupVersion.OpenAPIModels = openAPIModels
|
||||
apiGroupVersion.MaxRequestBodyBytes = s.maxRequestBodyBytes
|
||||
|
||||
if err := apiGroupVersion.InstallREST(s.Handler.GoRestfulContainer); err != nil {
|
||||
return fmt.Errorf("unable to setup API %v: %v", apiGroupInfo, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *GenericAPIServer) InstallLegacyAPIGroup(apiPrefix string, apiGroupInfo *APIGroupInfo) error {
|
||||
if !s.legacyAPIGroupPrefixes.Has(apiPrefix) {
|
||||
return fmt.Errorf("%q is not in the allowed legacy API prefixes: %v", apiPrefix, s.legacyAPIGroupPrefixes.List())
|
||||
}
|
||||
|
||||
openAPIModels, err := s.getOpenAPIModels(apiPrefix, apiGroupInfo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get openapi models: %v", err)
|
||||
}
|
||||
|
||||
if err := s.installAPIResources(apiPrefix, apiGroupInfo, openAPIModels); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Install the version handler.
|
||||
// Add a handler at /<apiPrefix> to enumerate the supported api versions.
|
||||
s.Handler.GoRestfulContainer.Add(discovery.NewLegacyRootAPIHandler(s.discoveryAddresses, s.Serializer, apiPrefix).WebService())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exposes given api groups in the API.
|
||||
func (s *GenericAPIServer) InstallAPIGroups(apiGroupInfos ...*APIGroupInfo) error {
|
||||
for _, apiGroupInfo := range apiGroupInfos {
|
||||
// Do not register empty group or empty version. Doing so claims /apis/ for the wrong entity to be returned.
|
||||
// Catching these here places the error much closer to its origin
|
||||
if len(apiGroupInfo.PrioritizedVersions[0].Group) == 0 {
|
||||
return fmt.Errorf("cannot register handler with an empty group for %#v", *apiGroupInfo)
|
||||
}
|
||||
if len(apiGroupInfo.PrioritizedVersions[0].Version) == 0 {
|
||||
return fmt.Errorf("cannot register handler with an empty version for %#v", *apiGroupInfo)
|
||||
}
|
||||
}
|
||||
|
||||
openAPIModels, err := s.getOpenAPIModels(APIGroupPrefix, apiGroupInfos...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to get openapi models: %v", err)
|
||||
}
|
||||
|
||||
for _, apiGroupInfo := range apiGroupInfos {
|
||||
if err := s.installAPIResources(APIGroupPrefix, apiGroupInfo, openAPIModels); err != nil {
|
||||
return fmt.Errorf("unable to install api resources: %v", err)
|
||||
}
|
||||
|
||||
// setup discovery
|
||||
// Install the version handler.
|
||||
// Add a handler at /apis/<groupName> to enumerate all versions supported by this group.
|
||||
apiVersionsForDiscovery := []metav1.GroupVersionForDiscovery{}
|
||||
for _, groupVersion := range apiGroupInfo.PrioritizedVersions {
|
||||
// Check the config to make sure that we elide versions that don't have any resources
|
||||
if len(apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version]) == 0 {
|
||||
continue
|
||||
}
|
||||
apiVersionsForDiscovery = append(apiVersionsForDiscovery, metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: groupVersion.String(),
|
||||
Version: groupVersion.Version,
|
||||
})
|
||||
}
|
||||
preferredVersionForDiscovery := metav1.GroupVersionForDiscovery{
|
||||
GroupVersion: apiGroupInfo.PrioritizedVersions[0].String(),
|
||||
Version: apiGroupInfo.PrioritizedVersions[0].Version,
|
||||
}
|
||||
apiGroup := metav1.APIGroup{
|
||||
Name: apiGroupInfo.PrioritizedVersions[0].Group,
|
||||
Versions: apiVersionsForDiscovery,
|
||||
PreferredVersion: preferredVersionForDiscovery,
|
||||
}
|
||||
|
||||
s.DiscoveryGroupManager.AddGroup(apiGroup)
|
||||
s.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup).WebService())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exposes the given api group in the API.
|
||||
func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error {
|
||||
return s.InstallAPIGroups(apiGroupInfo)
|
||||
}
|
||||
|
||||
func (s *GenericAPIServer) getAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion schema.GroupVersion, apiPrefix string) *genericapi.APIGroupVersion {
|
||||
storage := make(map[string]rest.Storage)
|
||||
for k, v := range apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version] {
|
||||
storage[strings.ToLower(k)] = v
|
||||
}
|
||||
version := s.newAPIGroupVersion(apiGroupInfo, groupVersion)
|
||||
version.Root = apiPrefix
|
||||
version.Storage = storage
|
||||
return version
|
||||
}
|
||||
|
||||
func (s *GenericAPIServer) newAPIGroupVersion(apiGroupInfo *APIGroupInfo, groupVersion schema.GroupVersion) *genericapi.APIGroupVersion {
|
||||
return &genericapi.APIGroupVersion{
|
||||
GroupVersion: groupVersion,
|
||||
MetaGroupVersion: apiGroupInfo.MetaGroupVersion,
|
||||
|
||||
ParameterCodec: apiGroupInfo.ParameterCodec,
|
||||
Serializer: apiGroupInfo.NegotiatedSerializer,
|
||||
Creater: apiGroupInfo.Scheme,
|
||||
Convertor: apiGroupInfo.Scheme,
|
||||
UnsafeConvertor: runtime.UnsafeObjectConvertor(apiGroupInfo.Scheme),
|
||||
Defaulter: apiGroupInfo.Scheme,
|
||||
Typer: apiGroupInfo.Scheme,
|
||||
Linker: runtime.SelfLinker(meta.NewAccessor()),
|
||||
|
||||
Admit: s.admissionControl,
|
||||
MinRequestTimeout: s.minRequestTimeout,
|
||||
EnableAPIResponseCompression: s.enableAPIResponseCompression,
|
||||
Authorizer: s.Authorizer,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultAPIGroupInfo returns an APIGroupInfo stubbed with "normal" values
|
||||
// exposed for easier composition from other packages
|
||||
func NewDefaultAPIGroupInfo(group string, scheme *runtime.Scheme, parameterCodec runtime.ParameterCodec, codecs serializer.CodecFactory) APIGroupInfo {
|
||||
return APIGroupInfo{
|
||||
PrioritizedVersions: scheme.PrioritizedVersionsForGroup(group),
|
||||
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{},
|
||||
// TODO unhardcode this. It was hardcoded before, but we need to re-evaluate
|
||||
OptionsExternalVersion: &schema.GroupVersion{Version: "v1"},
|
||||
Scheme: scheme,
|
||||
ParameterCodec: parameterCodec,
|
||||
NegotiatedSerializer: codecs,
|
||||
}
|
||||
}
|
||||
|
||||
// getOpenAPIModels is a private method for getting the OpenAPI models
|
||||
func (s *GenericAPIServer) getOpenAPIModels(apiPrefix string, apiGroupInfos ...*APIGroupInfo) (openapiproto.Models, error) {
|
||||
if s.openAPIConfig == nil {
|
||||
return nil, nil
|
||||
}
|
||||
pathsToIgnore := openapiutil.NewTrie(s.openAPIConfig.IgnorePrefixes)
|
||||
resourceNames := make([]string, 0)
|
||||
for _, apiGroupInfo := range apiGroupInfos {
|
||||
groupResources, err := getResourceNamesForGroup(apiPrefix, apiGroupInfo, pathsToIgnore)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resourceNames = append(resourceNames, groupResources...)
|
||||
}
|
||||
|
||||
// Build the openapi definitions for those resources and convert it to proto models
|
||||
openAPISpec, err := openapibuilder.BuildOpenAPIDefinitionsForResources(s.openAPIConfig, resourceNames...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return utilopenapi.ToProtoModels(openAPISpec)
|
||||
}
|
||||
|
||||
// getResourceNamesForGroup is a private method for getting the canonical names for each resource to build in an api group
|
||||
func getResourceNamesForGroup(apiPrefix string, apiGroupInfo *APIGroupInfo, pathsToIgnore openapiutil.Trie) ([]string, error) {
|
||||
// Get the canonical names of every resource we need to build in this api group
|
||||
resourceNames := make([]string, 0)
|
||||
for _, groupVersion := range apiGroupInfo.PrioritizedVersions {
|
||||
for resource, storage := range apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version] {
|
||||
path := gpath.Join(apiPrefix, groupVersion.Group, groupVersion.Version, resource)
|
||||
if !pathsToIgnore.HasPrefix(path) {
|
||||
kind, err := genericapi.GetResourceKind(groupVersion, storage, apiGroupInfo.Scheme)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sampleObject, err := apiGroupInfo.Scheme.New(kind)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
name := openapiutil.GetCanonicalTypeName(sampleObject)
|
||||
resourceNames = append(resourceNames, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return resourceNames, nil
|
||||
}
|
||||
190
vendor/k8s.io/apiserver/pkg/server/handler.go
generated
vendored
190
vendor/k8s.io/apiserver/pkg/server/handler.go
generated
vendored
|
|
@ -1,190 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
rt "runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"k8s.io/klog"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
)
|
||||
|
||||
// APIServerHandlers holds the different http.Handlers used by the API server.
|
||||
// This includes the full handler chain, the director (which chooses between gorestful and nonGoRestful,
|
||||
// the gorestful handler (used for the API) which falls through to the nonGoRestful handler on unregistered paths,
|
||||
// and the nonGoRestful handler (which can contain a fallthrough of its own)
|
||||
// FullHandlerChain -> Director -> {GoRestfulContainer,NonGoRestfulMux} based on inspection of registered web services
|
||||
type APIServerHandler struct {
|
||||
// FullHandlerChain is the one that is eventually served with. It should include the full filter
|
||||
// chain and then call the Director.
|
||||
FullHandlerChain http.Handler
|
||||
// The registered APIs. InstallAPIs uses this. Other servers probably shouldn't access this directly.
|
||||
GoRestfulContainer *restful.Container
|
||||
// NonGoRestfulMux is the final HTTP handler in the chain.
|
||||
// It comes after all filters and the API handling
|
||||
// This is where other servers can attach handler to various parts of the chain.
|
||||
NonGoRestfulMux *mux.PathRecorderMux
|
||||
|
||||
// Director is here so that we can properly handle fall through and proxy cases.
|
||||
// This looks a bit bonkers, but here's what's happening. We need to have /apis handling registered in gorestful in order to have
|
||||
// swagger generated for compatibility. Doing that with `/apis` as a webservice, means that it forcibly 404s (no defaulting allowed)
|
||||
// all requests which are not /apis or /apis/. We need those calls to fall through behind goresful for proper delegation. Trying to
|
||||
// register for a pattern which includes everything behind it doesn't work because gorestful negotiates for verbs and content encoding
|
||||
// and all those things go crazy when gorestful really just needs to pass through. In addition, openapi enforces unique verb constraints
|
||||
// which we don't fit into and it still muddies up swagger. Trying to switch the webservices into a route doesn't work because the
|
||||
// containing webservice faces all the same problems listed above.
|
||||
// This leads to the crazy thing done here. Our mux does what we need, so we'll place it in front of gorestful. It will introspect to
|
||||
// decide if the route is likely to be handled by goresful and route there if needed. Otherwise, it goes to PostGoRestful mux in
|
||||
// order to handle "normal" paths and delegation. Hopefully no API consumers will ever have to deal with this level of detail. I think
|
||||
// we should consider completely removing gorestful.
|
||||
// Other servers should only use this opaquely to delegate to an API server.
|
||||
Director http.Handler
|
||||
}
|
||||
|
||||
// HandlerChainBuilderFn is used to wrap the GoRestfulContainer handler using the provided handler chain.
|
||||
// It is normally used to apply filtering like authentication and authorization
|
||||
type HandlerChainBuilderFn func(apiHandler http.Handler) http.Handler
|
||||
|
||||
func NewAPIServerHandler(name string, s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler {
|
||||
nonGoRestfulMux := mux.NewPathRecorderMux(name)
|
||||
if notFoundHandler != nil {
|
||||
nonGoRestfulMux.NotFoundHandler(notFoundHandler)
|
||||
}
|
||||
|
||||
gorestfulContainer := restful.NewContainer()
|
||||
gorestfulContainer.ServeMux = http.NewServeMux()
|
||||
gorestfulContainer.Router(restful.CurlyRouter{}) // e.g. for proxy/{kind}/{name}/{*}
|
||||
gorestfulContainer.RecoverHandler(func(panicReason interface{}, httpWriter http.ResponseWriter) {
|
||||
logStackOnRecover(s, panicReason, httpWriter)
|
||||
})
|
||||
gorestfulContainer.ServiceErrorHandler(func(serviceErr restful.ServiceError, request *restful.Request, response *restful.Response) {
|
||||
serviceErrorHandler(s, serviceErr, request, response)
|
||||
})
|
||||
|
||||
director := director{
|
||||
name: name,
|
||||
goRestfulContainer: gorestfulContainer,
|
||||
nonGoRestfulMux: nonGoRestfulMux,
|
||||
}
|
||||
|
||||
return &APIServerHandler{
|
||||
FullHandlerChain: handlerChainBuilder(director),
|
||||
GoRestfulContainer: gorestfulContainer,
|
||||
NonGoRestfulMux: nonGoRestfulMux,
|
||||
Director: director,
|
||||
}
|
||||
}
|
||||
|
||||
// ListedPaths returns the paths that should be shown under /
|
||||
func (a *APIServerHandler) ListedPaths() []string {
|
||||
var handledPaths []string
|
||||
// Extract the paths handled using restful.WebService
|
||||
for _, ws := range a.GoRestfulContainer.RegisteredWebServices() {
|
||||
handledPaths = append(handledPaths, ws.RootPath())
|
||||
}
|
||||
handledPaths = append(handledPaths, a.NonGoRestfulMux.ListedPaths()...)
|
||||
sort.Strings(handledPaths)
|
||||
|
||||
return handledPaths
|
||||
}
|
||||
|
||||
type director struct {
|
||||
name string
|
||||
goRestfulContainer *restful.Container
|
||||
nonGoRestfulMux *mux.PathRecorderMux
|
||||
}
|
||||
|
||||
func (d director) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
||||
path := req.URL.Path
|
||||
|
||||
// check to see if our webservices want to claim this path
|
||||
for _, ws := range d.goRestfulContainer.RegisteredWebServices() {
|
||||
switch {
|
||||
case ws.RootPath() == "/apis":
|
||||
// if we are exactly /apis or /apis/, then we need special handling in loop.
|
||||
// normally these are passed to the nonGoRestfulMux, but if discovery is enabled, it will go directly.
|
||||
// We can't rely on a prefix match since /apis matches everything (see the big comment on Director above)
|
||||
if path == "/apis" || path == "/apis/" {
|
||||
klog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath())
|
||||
// don't use servemux here because gorestful servemuxes get messed up when removing webservices
|
||||
// TODO fix gorestful, remove TPRs, or stop using gorestful
|
||||
d.goRestfulContainer.Dispatch(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
case strings.HasPrefix(path, ws.RootPath()):
|
||||
// ensure an exact match or a path boundary match
|
||||
if len(path) == len(ws.RootPath()) || path[len(ws.RootPath())] == '/' {
|
||||
klog.V(5).Infof("%v: %v %q satisfied by gorestful with webservice %v", d.name, req.Method, path, ws.RootPath())
|
||||
// don't use servemux here because gorestful servemuxes get messed up when removing webservices
|
||||
// TODO fix gorestful, remove TPRs, or stop using gorestful
|
||||
d.goRestfulContainer.Dispatch(w, req)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we didn't find a match, then we just skip gorestful altogether
|
||||
klog.V(5).Infof("%v: %v %q satisfied by nonGoRestful", d.name, req.Method, path)
|
||||
d.nonGoRestfulMux.ServeHTTP(w, req)
|
||||
}
|
||||
|
||||
//TODO: Unify with RecoverPanics?
|
||||
func logStackOnRecover(s runtime.NegotiatedSerializer, panicReason interface{}, w http.ResponseWriter) {
|
||||
var buffer bytes.Buffer
|
||||
buffer.WriteString(fmt.Sprintf("recover from panic situation: - %v\r\n", panicReason))
|
||||
for i := 2; ; i++ {
|
||||
_, file, line, ok := rt.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(" %s:%d\r\n", file, line))
|
||||
}
|
||||
klog.Errorln(buffer.String())
|
||||
|
||||
headers := http.Header{}
|
||||
if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
|
||||
headers.Set("Accept", ct)
|
||||
}
|
||||
responsewriters.ErrorNegotiated(apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers})
|
||||
}
|
||||
|
||||
func serviceErrorHandler(s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) {
|
||||
responsewriters.ErrorNegotiated(
|
||||
apierrors.NewGenericServerResponse(serviceErr.Code, "", schema.GroupResource{}, "", serviceErr.Message, 0, false),
|
||||
s,
|
||||
schema.GroupVersion{},
|
||||
resp,
|
||||
request.Request,
|
||||
)
|
||||
}
|
||||
|
||||
// ServeHTTP makes it an http.Handler
|
||||
func (a *APIServerHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
a.FullHandlerChain.ServeHTTP(w, r)
|
||||
}
|
||||
45
vendor/k8s.io/apiserver/pkg/server/healthz.go
generated
vendored
45
vendor/k8s.io/apiserver/pkg/server/healthz.go
generated
vendored
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
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 server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
)
|
||||
|
||||
// AddHealthzCheck allows you to add a HealthzCheck.
|
||||
func (s *GenericAPIServer) AddHealthzChecks(checks ...healthz.HealthzChecker) error {
|
||||
s.healthzLock.Lock()
|
||||
defer s.healthzLock.Unlock()
|
||||
|
||||
if s.healthzCreated {
|
||||
return fmt.Errorf("unable to add because the healthz endpoint has already been created")
|
||||
}
|
||||
|
||||
s.healthzChecks = append(s.healthzChecks, checks...)
|
||||
return nil
|
||||
}
|
||||
|
||||
// installHealthz creates the healthz endpoint for this server
|
||||
func (s *GenericAPIServer) installHealthz() {
|
||||
s.healthzLock.Lock()
|
||||
defer s.healthzLock.Unlock()
|
||||
s.healthzCreated = true
|
||||
|
||||
healthz.InstallHandler(s.Handler.NonGoRestfulMux, s.healthzChecks...)
|
||||
}
|
||||
237
vendor/k8s.io/apiserver/pkg/server/hooks.go
generated
vendored
237
vendor/k8s.io/apiserver/pkg/server/hooks.go
generated
vendored
|
|
@ -1,237 +0,0 @@
|
|||
/*
|
||||
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 server
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
|
||||
"k8s.io/klog"
|
||||
|
||||
utilerrors "k8s.io/apimachinery/pkg/util/errors"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// PostStartHookFunc is a function that is called after the server has started.
|
||||
// It must properly handle cases like:
|
||||
// 1. asynchronous start in multiple API server processes
|
||||
// 2. conflicts between the different processes all trying to perform the same action
|
||||
// 3. partially complete work (API server crashes while running your hook)
|
||||
// 4. API server access **BEFORE** your hook has completed
|
||||
// Think of it like a mini-controller that is super privileged and gets to run in-process
|
||||
// If you use this feature, tag @deads2k on github who has promised to review code for anyone's PostStartHook
|
||||
// until it becomes easier to use.
|
||||
type PostStartHookFunc func(context PostStartHookContext) error
|
||||
|
||||
// PreShutdownHookFunc is a function that can be added to the shutdown logic.
|
||||
type PreShutdownHookFunc func() error
|
||||
|
||||
// PostStartHookContext provides information about this API server to a PostStartHookFunc
|
||||
type PostStartHookContext struct {
|
||||
// LoopbackClientConfig is a config for a privileged loopback connection to the API server
|
||||
LoopbackClientConfig *restclient.Config
|
||||
// StopCh is the channel that will be closed when the server stops
|
||||
StopCh <-chan struct{}
|
||||
}
|
||||
|
||||
// PostStartHookProvider is an interface in addition to provide a post start hook for the api server
|
||||
type PostStartHookProvider interface {
|
||||
PostStartHook() (string, PostStartHookFunc, error)
|
||||
}
|
||||
|
||||
type postStartHookEntry struct {
|
||||
hook PostStartHookFunc
|
||||
// originatingStack holds the stack that registered postStartHooks. This allows us to show a more helpful message
|
||||
// for duplicate registration.
|
||||
originatingStack string
|
||||
|
||||
// done will be closed when the postHook is finished
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
type preShutdownHookEntry struct {
|
||||
hook PreShutdownHookFunc
|
||||
}
|
||||
|
||||
// AddPostStartHook allows you to add a PostStartHook.
|
||||
func (s *GenericAPIServer) AddPostStartHook(name string, hook PostStartHookFunc) error {
|
||||
if len(name) == 0 {
|
||||
return fmt.Errorf("missing name")
|
||||
}
|
||||
if hook == nil {
|
||||
return nil
|
||||
}
|
||||
if s.disabledPostStartHooks.Has(name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.postStartHookLock.Lock()
|
||||
defer s.postStartHookLock.Unlock()
|
||||
|
||||
if s.postStartHooksCalled {
|
||||
return fmt.Errorf("unable to add %q because PostStartHooks have already been called", name)
|
||||
}
|
||||
if postStartHook, exists := s.postStartHooks[name]; exists {
|
||||
// this is programmer error, but it can be hard to debug
|
||||
return fmt.Errorf("unable to add %q because it was already registered by: %s", name, postStartHook.originatingStack)
|
||||
}
|
||||
|
||||
// done is closed when the poststarthook is finished. This is used by the health check to be able to indicate
|
||||
// that the poststarthook is finished
|
||||
done := make(chan struct{})
|
||||
if err := s.AddHealthzChecks(postStartHookHealthz{name: "poststarthook/" + name, done: done}); err != nil {
|
||||
return err
|
||||
}
|
||||
s.postStartHooks[name] = postStartHookEntry{hook: hook, originatingStack: string(debug.Stack()), done: done}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddPostStartHookOrDie allows you to add a PostStartHook, but dies on failure
|
||||
func (s *GenericAPIServer) AddPostStartHookOrDie(name string, hook PostStartHookFunc) {
|
||||
if err := s.AddPostStartHook(name, hook); err != nil {
|
||||
klog.Fatalf("Error registering PostStartHook %q: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// AddPreShutdownHook allows you to add a PreShutdownHook.
|
||||
func (s *GenericAPIServer) AddPreShutdownHook(name string, hook PreShutdownHookFunc) error {
|
||||
if len(name) == 0 {
|
||||
return fmt.Errorf("missing name")
|
||||
}
|
||||
if hook == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.preShutdownHookLock.Lock()
|
||||
defer s.preShutdownHookLock.Unlock()
|
||||
|
||||
if s.preShutdownHooksCalled {
|
||||
return fmt.Errorf("unable to add %q because PreShutdownHooks have already been called", name)
|
||||
}
|
||||
if _, exists := s.preShutdownHooks[name]; exists {
|
||||
return fmt.Errorf("unable to add %q because it is already registered", name)
|
||||
}
|
||||
|
||||
s.preShutdownHooks[name] = preShutdownHookEntry{hook: hook}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddPreShutdownHookOrDie allows you to add a PostStartHook, but dies on failure
|
||||
func (s *GenericAPIServer) AddPreShutdownHookOrDie(name string, hook PreShutdownHookFunc) {
|
||||
if err := s.AddPreShutdownHook(name, hook); err != nil {
|
||||
klog.Fatalf("Error registering PreShutdownHook %q: %v", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
// RunPostStartHooks runs the PostStartHooks for the server
|
||||
func (s *GenericAPIServer) RunPostStartHooks(stopCh <-chan struct{}) {
|
||||
s.postStartHookLock.Lock()
|
||||
defer s.postStartHookLock.Unlock()
|
||||
s.postStartHooksCalled = true
|
||||
|
||||
context := PostStartHookContext{
|
||||
LoopbackClientConfig: s.LoopbackClientConfig,
|
||||
StopCh: stopCh,
|
||||
}
|
||||
|
||||
for hookName, hookEntry := range s.postStartHooks {
|
||||
go runPostStartHook(hookName, hookEntry, context)
|
||||
}
|
||||
}
|
||||
|
||||
// RunPreShutdownHooks runs the PreShutdownHooks for the server
|
||||
func (s *GenericAPIServer) RunPreShutdownHooks() error {
|
||||
var errorList []error
|
||||
|
||||
s.preShutdownHookLock.Lock()
|
||||
defer s.preShutdownHookLock.Unlock()
|
||||
s.preShutdownHooksCalled = true
|
||||
|
||||
for hookName, hookEntry := range s.preShutdownHooks {
|
||||
if err := runPreShutdownHook(hookName, hookEntry); err != nil {
|
||||
errorList = append(errorList, err)
|
||||
}
|
||||
}
|
||||
return utilerrors.NewAggregate(errorList)
|
||||
}
|
||||
|
||||
// isPostStartHookRegistered checks whether a given PostStartHook is registered
|
||||
func (s *GenericAPIServer) isPostStartHookRegistered(name string) bool {
|
||||
s.postStartHookLock.Lock()
|
||||
defer s.postStartHookLock.Unlock()
|
||||
_, exists := s.postStartHooks[name]
|
||||
return exists
|
||||
}
|
||||
|
||||
func runPostStartHook(name string, entry postStartHookEntry, context PostStartHookContext) {
|
||||
var err error
|
||||
func() {
|
||||
// don't let the hook *accidentally* panic and kill the server
|
||||
defer utilruntime.HandleCrash()
|
||||
err = entry.hook(context)
|
||||
}()
|
||||
// if the hook intentionally wants to kill server, let it.
|
||||
if err != nil {
|
||||
klog.Fatalf("PostStartHook %q failed: %v", name, err)
|
||||
}
|
||||
close(entry.done)
|
||||
}
|
||||
|
||||
func runPreShutdownHook(name string, entry preShutdownHookEntry) error {
|
||||
var err error
|
||||
func() {
|
||||
// don't let the hook *accidentally* panic and kill the server
|
||||
defer utilruntime.HandleCrash()
|
||||
err = entry.hook()
|
||||
}()
|
||||
if err != nil {
|
||||
return fmt.Errorf("PreShutdownHook %q failed: %v", name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// postStartHookHealthz implements a healthz check for poststarthooks. It will return a "hookNotFinished"
|
||||
// error until the poststarthook is finished.
|
||||
type postStartHookHealthz struct {
|
||||
name string
|
||||
|
||||
// done will be closed when the postStartHook is finished
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
var _ healthz.HealthzChecker = postStartHookHealthz{}
|
||||
|
||||
func (h postStartHookHealthz) Name() string {
|
||||
return h.name
|
||||
}
|
||||
|
||||
var hookNotFinished = errors.New("not finished")
|
||||
|
||||
func (h postStartHookHealthz) Check(req *http.Request) error {
|
||||
select {
|
||||
case <-h.done:
|
||||
return nil
|
||||
default:
|
||||
return hookNotFinished
|
||||
}
|
||||
}
|
||||
32
vendor/k8s.io/apiserver/pkg/server/plugins.go
generated
vendored
32
vendor/k8s.io/apiserver/pkg/server/plugins.go
generated
vendored
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 server
|
||||
|
||||
// This file exists to force the desired plugin implementations to be linked into genericapi pkg.
|
||||
import (
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle"
|
||||
mutatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
|
||||
validatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
|
||||
)
|
||||
|
||||
// RegisterAllAdmissionPlugins registers all admission plugins
|
||||
func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
|
||||
lifecycle.Register(plugins)
|
||||
validatingwebhook.Register(plugins)
|
||||
mutatingwebhook.Register(plugins)
|
||||
}
|
||||
238
vendor/k8s.io/apiserver/pkg/server/secure_serving.go
generated
vendored
238
vendor/k8s.io/apiserver/pkg/server/secure_serving.go
generated
vendored
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
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 server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
"k8s.io/klog"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultKeepAlivePeriod = 3 * time.Minute
|
||||
)
|
||||
|
||||
// Serve runs the secure http server. It fails only if certificates cannot be loaded or the initial listen call fails.
|
||||
// The actual server loop (stoppable by closing stopCh) runs in a go routine, i.e. Serve does not block.
|
||||
// It returns a stoppedCh that is closed when all non-hijacked active requests have been processed.
|
||||
func (s *SecureServingInfo) Serve(handler http.Handler, shutdownTimeout time.Duration, stopCh <-chan struct{}) (<-chan struct{}, error) {
|
||||
if s.Listener == nil {
|
||||
return nil, fmt.Errorf("listener must not be nil")
|
||||
}
|
||||
|
||||
secureServer := &http.Server{
|
||||
Addr: s.Listener.Addr().String(),
|
||||
Handler: handler,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
TLSConfig: &tls.Config{
|
||||
NameToCertificate: s.SNICerts,
|
||||
// Can't use SSLv3 because of POODLE and BEAST
|
||||
// Can't use TLSv1.0 because of POODLE and BEAST using CBC cipher
|
||||
// Can't use TLSv1.1 because of RC4 cipher usage
|
||||
MinVersion: tls.VersionTLS12,
|
||||
// enable HTTP2 for go's 1.7 HTTP Server
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
},
|
||||
}
|
||||
|
||||
if s.MinTLSVersion > 0 {
|
||||
secureServer.TLSConfig.MinVersion = s.MinTLSVersion
|
||||
}
|
||||
if len(s.CipherSuites) > 0 {
|
||||
secureServer.TLSConfig.CipherSuites = s.CipherSuites
|
||||
}
|
||||
|
||||
if s.Cert != nil {
|
||||
secureServer.TLSConfig.Certificates = []tls.Certificate{*s.Cert}
|
||||
}
|
||||
|
||||
// append all named certs. Otherwise, the go tls stack will think no SNI processing
|
||||
// is necessary because there is only one cert anyway.
|
||||
// Moreover, if ServerCert.CertFile/ServerCert.KeyFile are not set, the first SNI
|
||||
// cert will become the default cert. That's what we expect anyway.
|
||||
for _, c := range s.SNICerts {
|
||||
secureServer.TLSConfig.Certificates = append(secureServer.TLSConfig.Certificates, *c)
|
||||
}
|
||||
|
||||
if s.ClientCA != nil {
|
||||
// Populate PeerCertificates in requests, but don't reject connections without certificates
|
||||
// This allows certificates to be validated by authenticators, while still allowing other auth types
|
||||
secureServer.TLSConfig.ClientAuth = tls.RequestClientCert
|
||||
// Specify allowed CAs for client certificates
|
||||
secureServer.TLSConfig.ClientCAs = s.ClientCA
|
||||
}
|
||||
|
||||
// At least 99% of serialized resources in surveyed clusters were smaller than 256kb.
|
||||
// This should be big enough to accommodate most API POST requests in a single frame,
|
||||
// and small enough to allow a per connection buffer of this size multiplied by `MaxConcurrentStreams`.
|
||||
const resourceBody99Percentile = 256 * 1024
|
||||
|
||||
http2Options := &http2.Server{}
|
||||
|
||||
// shrink the per-stream buffer and max framesize from the 1MB default while still accommodating most API POST requests in a single frame
|
||||
http2Options.MaxUploadBufferPerStream = resourceBody99Percentile
|
||||
http2Options.MaxReadFrameSize = resourceBody99Percentile
|
||||
|
||||
// use the overridden concurrent streams setting or make the default of 250 explicit so we can size MaxUploadBufferPerConnection appropriately
|
||||
if s.HTTP2MaxStreamsPerConnection > 0 {
|
||||
http2Options.MaxConcurrentStreams = uint32(s.HTTP2MaxStreamsPerConnection)
|
||||
} else {
|
||||
http2Options.MaxConcurrentStreams = 250
|
||||
}
|
||||
|
||||
// increase the connection buffer size from the 1MB default to handle the specified number of concurrent streams
|
||||
http2Options.MaxUploadBufferPerConnection = http2Options.MaxUploadBufferPerStream * int32(http2Options.MaxConcurrentStreams)
|
||||
|
||||
// apply settings to the server
|
||||
if err := http2.ConfigureServer(secureServer, http2Options); err != nil {
|
||||
return nil, fmt.Errorf("error configuring http2: %v", err)
|
||||
}
|
||||
|
||||
klog.Infof("Serving securely on %s", secureServer.Addr)
|
||||
return RunServer(secureServer, s.Listener, shutdownTimeout, stopCh)
|
||||
}
|
||||
|
||||
// RunServer listens on the given port if listener is not given,
|
||||
// then spawns a go-routine continuously serving until the stopCh is closed.
|
||||
// It returns a stoppedCh that is closed when all non-hijacked active requests
|
||||
// have been processed.
|
||||
// This function does not block
|
||||
// TODO: make private when insecure serving is gone from the kube-apiserver
|
||||
func RunServer(
|
||||
server *http.Server,
|
||||
ln net.Listener,
|
||||
shutDownTimeout time.Duration,
|
||||
stopCh <-chan struct{},
|
||||
) (<-chan struct{}, error) {
|
||||
if ln == nil {
|
||||
return nil, fmt.Errorf("listener must not be nil")
|
||||
}
|
||||
|
||||
// Shutdown server gracefully.
|
||||
stoppedCh := make(chan struct{})
|
||||
go func() {
|
||||
defer close(stoppedCh)
|
||||
<-stopCh
|
||||
ctx, cancel := context.WithTimeout(context.Background(), shutDownTimeout)
|
||||
server.Shutdown(ctx)
|
||||
cancel()
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer utilruntime.HandleCrash()
|
||||
|
||||
var listener net.Listener
|
||||
listener = tcpKeepAliveListener{ln.(*net.TCPListener)}
|
||||
if server.TLSConfig != nil {
|
||||
listener = tls.NewListener(listener, server.TLSConfig)
|
||||
}
|
||||
|
||||
err := server.Serve(listener)
|
||||
|
||||
msg := fmt.Sprintf("Stopped listening on %s", ln.Addr().String())
|
||||
select {
|
||||
case <-stopCh:
|
||||
klog.Info(msg)
|
||||
default:
|
||||
panic(fmt.Sprintf("%s due to error: %v", msg, err))
|
||||
}
|
||||
}()
|
||||
|
||||
return stoppedCh, nil
|
||||
}
|
||||
|
||||
type NamedTLSCert struct {
|
||||
TLSCert tls.Certificate
|
||||
|
||||
// Names is a list of domain patterns: fully qualified domain names, possibly prefixed with
|
||||
// wildcard segments.
|
||||
Names []string
|
||||
}
|
||||
|
||||
// GetNamedCertificateMap returns a map of *tls.Certificate by name. It's
|
||||
// suitable for use in tls.Config#NamedCertificates. Returns an error if any of the certs
|
||||
// cannot be loaded. Returns nil if len(certs) == 0
|
||||
func GetNamedCertificateMap(certs []NamedTLSCert) (map[string]*tls.Certificate, error) {
|
||||
// register certs with implicit names first, reverse order such that earlier trump over the later
|
||||
byName := map[string]*tls.Certificate{}
|
||||
for i := len(certs) - 1; i >= 0; i-- {
|
||||
if len(certs[i].Names) > 0 {
|
||||
continue
|
||||
}
|
||||
cert := &certs[i].TLSCert
|
||||
|
||||
// read names from certificate common names and DNS names
|
||||
if len(cert.Certificate) == 0 {
|
||||
return nil, fmt.Errorf("empty SNI certificate, skipping")
|
||||
}
|
||||
x509Cert, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse error for SNI certificate: %v", err)
|
||||
}
|
||||
cn := x509Cert.Subject.CommonName
|
||||
if cn == "*" || len(validation.IsDNS1123Subdomain(strings.TrimPrefix(cn, "*."))) == 0 {
|
||||
byName[cn] = cert
|
||||
}
|
||||
for _, san := range x509Cert.DNSNames {
|
||||
byName[san] = cert
|
||||
}
|
||||
// intentionally all IPs in the cert are ignored as SNI forbids passing IPs
|
||||
// to select a cert. Before go 1.6 the tls happily passed IPs as SNI values.
|
||||
}
|
||||
|
||||
// register certs with explicit names last, overwriting every of the implicit ones,
|
||||
// again in reverse order.
|
||||
for i := len(certs) - 1; i >= 0; i-- {
|
||||
namedCert := &certs[i]
|
||||
for _, name := range namedCert.Names {
|
||||
byName[name] = &certs[i].TLSCert
|
||||
}
|
||||
}
|
||||
|
||||
return byName, nil
|
||||
}
|
||||
|
||||
// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
|
||||
// connections. It's used by ListenAndServe and ListenAndServeTLS so
|
||||
// dead TCP connections (e.g. closing laptop mid-download) eventually
|
||||
// go away.
|
||||
//
|
||||
// Copied from Go 1.7.2 net/http/server.go
|
||||
type tcpKeepAliveListener struct {
|
||||
*net.TCPListener
|
||||
}
|
||||
|
||||
func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
|
||||
tc, err := ln.AcceptTCP()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tc.SetKeepAlive(true)
|
||||
tc.SetKeepAlivePeriod(defaultKeepAlivePeriod)
|
||||
return tc, nil
|
||||
}
|
||||
59
vendor/k8s.io/apiserver/pkg/server/signal.go
generated
vendored
59
vendor/k8s.io/apiserver/pkg/server/signal.go
generated
vendored
|
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 server
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
)
|
||||
|
||||
var onlyOneSignalHandler = make(chan struct{})
|
||||
var shutdownHandler chan os.Signal
|
||||
|
||||
// SetupSignalHandler registered for SIGTERM and SIGINT. A stop channel is returned
|
||||
// which is closed on one of these signals. If a second signal is caught, the program
|
||||
// is terminated with exit code 1.
|
||||
func SetupSignalHandler() <-chan struct{} {
|
||||
close(onlyOneSignalHandler) // panics when called twice
|
||||
|
||||
shutdownHandler = make(chan os.Signal, 2)
|
||||
|
||||
stop := make(chan struct{})
|
||||
signal.Notify(shutdownHandler, shutdownSignals...)
|
||||
go func() {
|
||||
<-shutdownHandler
|
||||
close(stop)
|
||||
<-shutdownHandler
|
||||
os.Exit(1) // second signal. Exit directly.
|
||||
}()
|
||||
|
||||
return stop
|
||||
}
|
||||
|
||||
// RequestShutdown emulates a received event that is considered as shutdown signal (SIGTERM/SIGINT)
|
||||
// This returns whether a handler was notified
|
||||
func RequestShutdown() bool {
|
||||
if shutdownHandler != nil {
|
||||
select {
|
||||
case shutdownHandler <- shutdownSignals[0]:
|
||||
return true
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
26
vendor/k8s.io/apiserver/pkg/server/signal_posix.go
generated
vendored
26
vendor/k8s.io/apiserver/pkg/server/signal_posix.go
generated
vendored
|
|
@ -1,26 +0,0 @@
|
|||
// +build !windows
|
||||
|
||||
/*
|
||||
Copyright 2017 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 server
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var shutdownSignals = []os.Signal{os.Interrupt, syscall.SIGTERM}
|
||||
23
vendor/k8s.io/apiserver/pkg/server/signal_windows.go
generated
vendored
23
vendor/k8s.io/apiserver/pkg/server/signal_windows.go
generated
vendored
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
Copyright 2017 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 server
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
var shutdownSignals = []os.Signal{os.Interrupt}
|
||||
Loading…
Add table
Add a link
Reference in a new issue