Replace godep with dep
This commit is contained in:
parent
1e7489927c
commit
bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions
135
vendor/k8s.io/apiserver/pkg/server/BUILD
generated
vendored
Normal file
135
vendor/k8s.io/apiserver/pkg/server/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"config_selfclient_test.go",
|
||||
"config_test.go",
|
||||
"genericapiserver_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/example:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/etcd/testing:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"config.go",
|
||||
"config_selfclient.go",
|
||||
"doc.go",
|
||||
"genericapiserver.go",
|
||||
"handler.go",
|
||||
"healthz.go",
|
||||
"hooks.go",
|
||||
"plugins.go",
|
||||
"serve.go",
|
||||
"signal.go",
|
||||
"signal_posix.go",
|
||||
] + select({
|
||||
"@io_bazel_rules_go//go/platform:windows_amd64": [
|
||||
"signal_windows.go",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
deps = [
|
||||
"//vendor/github.com/coreos/go-systemd/daemon:go_default_library",
|
||||
"//vendor/github.com/emicklei/go-restful:go_default_library",
|
||||
"//vendor/github.com/emicklei/go-restful-swagger12:go_default_library",
|
||||
"//vendor/github.com/go-openapi/spec:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
"//vendor/github.com/pkg/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/apiserver/install:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/audit:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/audit/policy:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/request/union:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/union:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/discovery:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/openapi:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/rest:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/filters:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/routes:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/filters:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/healthz:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/httplog:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/mux:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/options:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/routes:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/storage:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
573
vendor/k8s.io/apiserver/pkg/server/config.go
generated
vendored
Normal file
573
vendor/k8s.io/apiserver/pkg/server/config.go
generated
vendored
Normal file
|
|
@ -0,0 +1,573 @@
|
|||
/*
|
||||
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"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
goruntime "runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/emicklei/go-restful-swagger12"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"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"
|
||||
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"
|
||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||
|
||||
// install apis
|
||||
_ "k8s.io/apiserver/pkg/apis/apiserver/install"
|
||||
)
|
||||
|
||||
const (
|
||||
// DefaultLegacyAPIPrefix is where the 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 {
|
||||
// SecureServingInfo is required to serve https
|
||||
SecureServingInfo *SecureServingInfo
|
||||
|
||||
// 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
|
||||
LoopbackClientConfig *restclient.Config
|
||||
// Authenticator determines which subject is making the request
|
||||
Authenticator authenticator.Request
|
||||
// Authorizer determines whether the subject is allowed to make the request based only
|
||||
// on the RequestURI
|
||||
Authorizer authorizer.Authorizer
|
||||
// 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
|
||||
|
||||
EnableSwaggerUI bool
|
||||
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
|
||||
// LegacyAuditWriter is the destination for audit logs. If nil, they will not be written.
|
||||
LegacyAuditWriter io.Writer
|
||||
// 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
|
||||
// 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
|
||||
// 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)
|
||||
// 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
|
||||
// RequestContextMapper maps requests to contexts. Exported so downstream consumers can provider their own mappers
|
||||
// TODO confirm that anyone downstream actually uses this and doesn't just need an accessor
|
||||
RequestContextMapper apirequest.RequestContextMapper
|
||||
// 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
|
||||
// SwaggerConfig will be used in generating Swagger spec. This is nil by default. Use DefaultSwaggerConfig for "working" defaults.
|
||||
SwaggerConfig *swagger.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
|
||||
// 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
|
||||
|
||||
//===========================================================================
|
||||
// values below here are targets for removal
|
||||
//===========================================================================
|
||||
|
||||
// The port on PublicAddress where a read-write server will be installed.
|
||||
// Defaults to 6443 if not set.
|
||||
ReadWritePort int
|
||||
// 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
|
||||
}
|
||||
|
||||
type SecureServingInfo struct {
|
||||
// BindAddress is the ip:port to serve on
|
||||
BindAddress string
|
||||
// BindNetwork is the type of network to bind to - defaults to "tcp", accepts "tcp",
|
||||
// "tcp4", and "tcp6".
|
||||
BindNetwork string
|
||||
|
||||
// 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
|
||||
|
||||
// CACert is an optional certificate authority used for the loopback connection of the Admission controllers.
|
||||
// If this is nil, the certificate authority is extracted from Cert or a matching SNI certificate.
|
||||
CACert *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
|
||||
}
|
||||
|
||||
// NewConfig returns a Config struct with the default values
|
||||
func NewConfig(codecs serializer.CodecFactory) *Config {
|
||||
return &Config{
|
||||
Serializer: codecs,
|
||||
ReadWritePort: 443,
|
||||
RequestContextMapper: apirequest.NewRequestContextMapper(),
|
||||
BuildHandlerChainFunc: DefaultBuildHandlerChain,
|
||||
LegacyAPIGroupPrefixes: sets.NewString(DefaultLegacyAPIPrefix),
|
||||
DisabledPostStartHooks: sets.NewString(),
|
||||
HealthzChecks: []healthz.HealthzChecker{healthz.PingHealthz},
|
||||
EnableIndex: true,
|
||||
EnableDiscovery: true,
|
||||
EnableProfiling: true,
|
||||
MaxRequestsInFlight: 400,
|
||||
MaxMutatingRequestsInFlight: 200,
|
||||
RequestTimeout: time.Duration(60) * time.Second,
|
||||
MinRequestTimeout: 1800,
|
||||
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, scheme *runtime.Scheme) *openapicommon.Config {
|
||||
defNamer := apiopenapi.NewDefinitionNamer(scheme)
|
||||
return &openapicommon.Config{
|
||||
ProtocolList: []string{"https"},
|
||||
IgnorePrefixes: []string{"/swaggerapi"},
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultSwaggerConfig returns a default configuration without WebServiceURL and
|
||||
// WebServices set.
|
||||
func DefaultSwaggerConfig() *swagger.Config {
|
||||
return &swagger.Config{
|
||||
ApiPath: "/swaggerapi",
|
||||
SwaggerPath: "/swaggerui/",
|
||||
SwaggerFilePath: "/swagger-ui/",
|
||||
SchemaFormatHandler: func(typeName string) string {
|
||||
switch typeName {
|
||||
case "metav1.Time", "*metav1.Time":
|
||||
return "date-time"
|
||||
}
|
||||
return ""
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Config) ApplyClientCert(clientCAFile string) (*Config, error) {
|
||||
if c.SecureServingInfo != nil {
|
||||
if len(clientCAFile) > 0 {
|
||||
clientCAs, err := certutil.CertsFromFile(clientCAFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
if c.SecureServingInfo.ClientCA == nil {
|
||||
c.SecureServingInfo.ClientCA = x509.NewCertPool()
|
||||
}
|
||||
for _, cert := range clientCAs {
|
||||
c.SecureServingInfo.ClientCA.AddCert(cert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c, 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 {
|
||||
hostAndPort := c.PublicAddress.String()
|
||||
if c.ReadWritePort != 0 {
|
||||
hostAndPort = net.JoinHostPort(hostAndPort, strconv.Itoa(c.ReadWritePort))
|
||||
}
|
||||
c.ExternalAddress = hostAndPort
|
||||
}
|
||||
if c.OpenAPIConfig != nil && 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",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
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.SwaggerConfig != nil && len(c.SwaggerConfig.WebServicesUrl) == 0 {
|
||||
if c.SecureServingInfo != nil {
|
||||
c.SwaggerConfig.WebServicesUrl = "https://" + c.ExternalAddress
|
||||
} else {
|
||||
c.SwaggerConfig.WebServicesUrl = "http://" + c.ExternalAddress
|
||||
}
|
||||
}
|
||||
if c.DiscoveryAddresses == nil {
|
||||
c.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: c.ExternalAddress}
|
||||
}
|
||||
|
||||
// If the loopbackclientconfig is specified AND it has a token for use against the API server
|
||||
// wrap the authenticator and authorizer in loopback authentication logic
|
||||
if c.Authenticator != nil && c.Authorizer != nil && c.LoopbackClientConfig != nil && len(c.LoopbackClientConfig.BearerToken) > 0 {
|
||||
privilegedLoopbackToken := c.LoopbackClientConfig.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)
|
||||
c.Authenticator = authenticatorunion.New(tokenAuthenticator, c.Authenticator)
|
||||
|
||||
tokenAuthorizer := authorizerfactory.NewPrivilegedGroups(user.SystemPrivilegedGroup)
|
||||
c.Authorizer = authorizerunion.New(tokenAuthorizer, c.Authorizer)
|
||||
}
|
||||
|
||||
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.
|
||||
func (c completedConfig) New(name string, delegationTarget DelegationTarget) (*GenericAPIServer, error) {
|
||||
// The delegationTarget and the config must agree on the RequestContextMapper
|
||||
|
||||
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.RequestContextMapper, c.Serializer, handlerChainBuilder, delegationTarget.UnprotectedHandler())
|
||||
|
||||
s := &GenericAPIServer{
|
||||
discoveryAddresses: c.DiscoveryAddresses,
|
||||
LoopbackClientConfig: c.LoopbackClientConfig,
|
||||
legacyAPIGroupPrefixes: c.LegacyAPIGroupPrefixes,
|
||||
admissionControl: c.AdmissionControl,
|
||||
requestContextMapper: c.RequestContextMapper,
|
||||
Serializer: c.Serializer,
|
||||
AuditBackend: c.AuditBackend,
|
||||
delegationTarget: delegationTarget,
|
||||
|
||||
minRequestTimeout: time.Duration(c.MinRequestTimeout) * time.Second,
|
||||
|
||||
SecureServingInfo: c.SecureServingInfo,
|
||||
ExternalAddress: c.ExternalAddress,
|
||||
|
||||
Handler: apiServerHandler,
|
||||
|
||||
listedPathProvider: apiServerHandler,
|
||||
|
||||
swaggerConfig: c.SwaggerConfig,
|
||||
openAPIConfig: c.OpenAPIConfig,
|
||||
|
||||
postStartHooks: map[string]postStartHookEntry{},
|
||||
disabledPostStartHooks: c.DisabledPostStartHooks,
|
||||
|
||||
healthzChecks: c.HealthzChecks,
|
||||
|
||||
DiscoveryGroupManager: discovery.NewRootAPIsHandler(c.DiscoveryAddresses, c.Serializer, c.RequestContextMapper),
|
||||
|
||||
enableAPIResponseCompression: c.EnableAPIResponseCompression,
|
||||
}
|
||||
|
||||
for k, v := range delegationTarget.PostStartHooks() {
|
||||
s.postStartHooks[k] = v
|
||||
}
|
||||
|
||||
genericApiServerHookName := "generic-apiserver-start-informers"
|
||||
if c.SharedInformerFactory != nil && !s.isHookRegistered(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.RequestContextMapper, c.Authorizer, c.Serializer)
|
||||
handler = genericfilters.WithMaxInFlightLimit(handler, c.MaxRequestsInFlight, c.MaxMutatingRequestsInFlight, c.RequestContextMapper, c.LongRunningFunc)
|
||||
handler = genericapifilters.WithImpersonation(handler, c.RequestContextMapper, c.Authorizer, c.Serializer)
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
||||
handler = genericapifilters.WithAudit(handler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicyChecker, c.LongRunningFunc)
|
||||
} else {
|
||||
handler = genericapifilters.WithLegacyAudit(handler, c.RequestContextMapper, c.LegacyAuditWriter)
|
||||
}
|
||||
failedHandler := genericapifilters.Unauthorized(c.RequestContextMapper, c.Serializer, c.SupportsBasicAuth)
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing) {
|
||||
failedHandler = genericapifilters.WithFailedAuthenticationAudit(failedHandler, c.RequestContextMapper, c.AuditBackend, c.AuditPolicyChecker)
|
||||
}
|
||||
handler = genericapifilters.WithAuthentication(handler, c.RequestContextMapper, c.Authenticator, failedHandler)
|
||||
handler = genericfilters.WithCORS(handler, c.CorsAllowedOriginList, nil, nil, nil, "true")
|
||||
handler = genericfilters.WithTimeoutForNonLongRunningRequests(handler, c.RequestContextMapper, c.LongRunningFunc, c.RequestTimeout)
|
||||
handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver, c.RequestContextMapper)
|
||||
handler = apirequest.WithRequestContext(handler, c.RequestContextMapper)
|
||||
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.SwaggerConfig != nil && c.EnableSwaggerUI {
|
||||
routes.SwaggerUI{}.Install(s.Handler.NonGoRestfulMux)
|
||||
}
|
||||
if c.EnableProfiling {
|
||||
routes.Profiling{}.Install(s.Handler.NonGoRestfulMux)
|
||||
if c.EnableContentionProfiling {
|
||||
goruntime.SetBlockProfileRate(1)
|
||||
}
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
83
vendor/k8s.io/apiserver/pkg/server/config_selfclient.go
generated
vendored
Normal file
83
vendor/k8s.io/apiserver/pkg/server/config_selfclient.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
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) NewLoopbackClientConfig(token string, loopbackCert []byte) (*restclient.Config, error) {
|
||||
if s == nil || (s.Cert == nil && len(s.SNICerts) == 0) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
host, port, err := LoopbackHostPort(s.BindAddress)
|
||||
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),
|
||||
BearerToken: token,
|
||||
// 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{
|
||||
ServerName: LoopbackClientServerNameOverride,
|
||||
CAData: loopbackCert,
|
||||
},
|
||||
}, 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)
|
||||
}
|
||||
|
||||
// Value is expected to be an IP or DNS name, not "0.0.0.0".
|
||||
if host == "0.0.0.0" {
|
||||
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() {
|
||||
host = ipnet.IP.String()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return host, port, nil
|
||||
}
|
||||
46
vendor/k8s.io/apiserver/pkg/server/config_selfclient_test.go
generated
vendored
Normal file
46
vendor/k8s.io/apiserver/pkg/server/config_selfclient_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
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 (
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLoopbackHostPort(t *testing.T) {
|
||||
host, port, err := LoopbackHostPort("1.2.3.4:443")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if host != "1.2.3.4" {
|
||||
t.Fatalf("expected 1.2.3.4 as host, got %q", host)
|
||||
}
|
||||
if port != "443" {
|
||||
t.Fatalf("expected 443 as port, got %q", port)
|
||||
}
|
||||
|
||||
host, port, err = LoopbackHostPort("0.0.0.0:443")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if ip := net.ParseIP(host); ip == nil || !ip.IsLoopback() {
|
||||
t.Fatalf("expected host to be loopback, got %q", host)
|
||||
}
|
||||
if port != "443" {
|
||||
t.Fatalf("expected 443 as port, got %q", port)
|
||||
}
|
||||
}
|
||||
157
vendor/k8s.io/apiserver/pkg/server/config_test.go
generated
vendored
Normal file
157
vendor/k8s.io/apiserver/pkg/server/config_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
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 (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
func TestNewWithDelegate(t *testing.T) {
|
||||
delegateConfig := NewConfig(codecs)
|
||||
delegateConfig.PublicAddress = net.ParseIP("192.168.10.4")
|
||||
delegateConfig.RequestContextMapper = genericapirequest.NewRequestContextMapper()
|
||||
delegateConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
|
||||
delegateConfig.LoopbackClientConfig = &rest.Config{}
|
||||
delegateConfig.SwaggerConfig = DefaultSwaggerConfig()
|
||||
clientset := fake.NewSimpleClientset()
|
||||
if clientset == nil {
|
||||
t.Fatal("unable to create fake client set")
|
||||
}
|
||||
|
||||
delegateHealthzCalled := false
|
||||
delegateConfig.HealthzChecks = append(delegateConfig.HealthzChecks, healthz.NamedCheck("delegate-health", func(r *http.Request) error {
|
||||
delegateHealthzCalled = true
|
||||
return fmt.Errorf("delegate failed healthcheck")
|
||||
}))
|
||||
|
||||
sharedInformers := informers.NewSharedInformerFactory(clientset, delegateConfig.LoopbackClientConfig.Timeout)
|
||||
delegateServer, err := delegateConfig.Complete(sharedInformers).New("test", EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
delegateServer.Handler.NonGoRestfulMux.HandleFunc("/foo", func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
})
|
||||
|
||||
delegateServer.AddPostStartHook("delegate-post-start-hook", func(context PostStartHookContext) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
// this wires up swagger
|
||||
delegateServer.PrepareRun()
|
||||
|
||||
wrappingConfig := NewConfig(codecs)
|
||||
wrappingConfig.PublicAddress = net.ParseIP("192.168.10.4")
|
||||
wrappingConfig.RequestContextMapper = genericapirequest.NewRequestContextMapper()
|
||||
wrappingConfig.LegacyAPIGroupPrefixes = sets.NewString("/api")
|
||||
wrappingConfig.LoopbackClientConfig = &rest.Config{}
|
||||
wrappingConfig.SwaggerConfig = DefaultSwaggerConfig()
|
||||
|
||||
wrappingHealthzCalled := false
|
||||
wrappingConfig.HealthzChecks = append(wrappingConfig.HealthzChecks, healthz.NamedCheck("wrapping-health", func(r *http.Request) error {
|
||||
wrappingHealthzCalled = true
|
||||
return fmt.Errorf("wrapping failed healthcheck")
|
||||
}))
|
||||
|
||||
sharedInformers = informers.NewSharedInformerFactory(clientset, wrappingConfig.LoopbackClientConfig.Timeout)
|
||||
wrappingServer, err := wrappingConfig.Complete(sharedInformers).New("test", delegateServer)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wrappingServer.Handler.NonGoRestfulMux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
})
|
||||
|
||||
wrappingServer.AddPostStartHook("wrapping-post-start-hook", func(context PostStartHookContext) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
defer close(stopCh)
|
||||
wrappingServer.PrepareRun()
|
||||
wrappingServer.RunPostStartHooks(stopCh)
|
||||
|
||||
server := httptest.NewServer(wrappingServer.Handler)
|
||||
defer server.Close()
|
||||
|
||||
checkPath(server.URL, http.StatusOK, `{
|
||||
"paths": [
|
||||
"/apis",
|
||||
"/bar",
|
||||
"/foo",
|
||||
"/healthz",
|
||||
"/healthz/delegate-health",
|
||||
"/healthz/ping",
|
||||
"/healthz/poststarthook/delegate-post-start-hook",
|
||||
"/healthz/poststarthook/generic-apiserver-start-informers",
|
||||
"/healthz/poststarthook/wrapping-post-start-hook",
|
||||
"/healthz/wrapping-health",
|
||||
"/swaggerapi"
|
||||
]
|
||||
}`, t)
|
||||
checkPath(server.URL+"/healthz", http.StatusInternalServerError, `[+]ping ok
|
||||
[-]wrapping-health failed: reason withheld
|
||||
[-]delegate-health failed: reason withheld
|
||||
[+]poststarthook/generic-apiserver-start-informers ok
|
||||
[+]poststarthook/delegate-post-start-hook ok
|
||||
[+]poststarthook/wrapping-post-start-hook ok
|
||||
healthz check failed
|
||||
`, t)
|
||||
|
||||
checkPath(server.URL+"/healthz/delegate-health", http.StatusInternalServerError, `internal server error: delegate failed healthcheck
|
||||
`, t)
|
||||
checkPath(server.URL+"/healthz/wrapping-health", http.StatusInternalServerError, `internal server error: wrapping failed healthcheck
|
||||
`, t)
|
||||
checkPath(server.URL+"/healthz/poststarthook/delegate-post-start-hook", http.StatusOK, `ok`, t)
|
||||
checkPath(server.URL+"/healthz/poststarthook/wrapping-post-start-hook", http.StatusOK, `ok`, t)
|
||||
checkPath(server.URL+"/foo", http.StatusForbidden, ``, t)
|
||||
checkPath(server.URL+"/bar", http.StatusUnauthorized, ``, t)
|
||||
}
|
||||
|
||||
func checkPath(url string, expectedStatusCode int, expectedBody string, t *testing.T) {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dump, _ := httputil.DumpResponse(resp, true)
|
||||
t.Log(string(dump))
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if e, a := expectedBody, string(body); e != a {
|
||||
t.Errorf("%q expected %v, got %v", url, e, a)
|
||||
}
|
||||
if e, a := expectedStatusCode, resp.StatusCode; e != a {
|
||||
t.Errorf("%q expected %v, got %v", url, e, a)
|
||||
}
|
||||
}
|
||||
18
vendor/k8s.io/apiserver/pkg/server/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/apiserver/pkg/server/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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
|
||||
65
vendor/k8s.io/apiserver/pkg/server/filters/BUILD
generated
vendored
Normal file
65
vendor/k8s.io/apiserver/pkg/server/filters/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"compression_test.go",
|
||||
"cors_test.go",
|
||||
"maxinflight_test.go",
|
||||
"timeout_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/filters:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"compression.go",
|
||||
"cors.go",
|
||||
"doc.go",
|
||||
"longrunning.go",
|
||||
"maxinflight.go",
|
||||
"timeout.go",
|
||||
"wrap.go",
|
||||
],
|
||||
deps = [
|
||||
"//vendor/github.com/emicklei/go-restful:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/user:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/httplog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
3
vendor/k8s.io/apiserver/pkg/server/filters/OWNERS
generated
vendored
Executable file
3
vendor/k8s.io/apiserver/pkg/server/filters/OWNERS
generated
vendored
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
reviewers:
|
||||
- sttts
|
||||
- dims
|
||||
183
vendor/k8s.io/apiserver/pkg/server/filters/compression.go
generated
vendored
Normal file
183
vendor/k8s.io/apiserver/pkg/server/filters/compression.go
generated
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"compress/zlib"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
// Compressor is an interface to compression writers
|
||||
type Compressor interface {
|
||||
io.WriteCloser
|
||||
Flush() error
|
||||
}
|
||||
|
||||
const (
|
||||
headerAcceptEncoding = "Accept-Encoding"
|
||||
headerContentEncoding = "Content-Encoding"
|
||||
|
||||
encodingGzip = "gzip"
|
||||
encodingDeflate = "deflate"
|
||||
)
|
||||
|
||||
// WithCompression wraps an http.Handler with the Compression Handler
|
||||
func WithCompression(handler http.Handler, ctxMapper request.RequestContextMapper) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
wantsCompression, encoding := wantsCompressedResponse(req, ctxMapper)
|
||||
w.Header().Set("Vary", "Accept-Encoding")
|
||||
if wantsCompression {
|
||||
compressionWriter, err := NewCompressionResponseWriter(w, encoding)
|
||||
if err != nil {
|
||||
handleError(w, req, err)
|
||||
runtime.HandleError(fmt.Errorf("failed to compress HTTP response: %v", err))
|
||||
return
|
||||
}
|
||||
compressionWriter.Header().Set("Content-Encoding", encoding)
|
||||
handler.ServeHTTP(compressionWriter, req)
|
||||
compressionWriter.(*compressionResponseWriter).Close()
|
||||
} else {
|
||||
handler.ServeHTTP(w, req)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// wantsCompressedResponse reads the Accept-Encoding header to see if and which encoding is requested.
|
||||
func wantsCompressedResponse(req *http.Request, ctxMapper request.RequestContextMapper) (bool, string) {
|
||||
// don't compress watches
|
||||
ctx, ok := ctxMapper.Get(req)
|
||||
if !ok {
|
||||
return false, ""
|
||||
}
|
||||
info, ok := request.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
return false, ""
|
||||
}
|
||||
if !info.IsResourceRequest {
|
||||
return false, ""
|
||||
}
|
||||
if info.Verb == "watch" {
|
||||
return false, ""
|
||||
}
|
||||
header := req.Header.Get(headerAcceptEncoding)
|
||||
gi := strings.Index(header, encodingGzip)
|
||||
zi := strings.Index(header, encodingDeflate)
|
||||
// use in order of appearance
|
||||
switch {
|
||||
case gi == -1:
|
||||
return zi != -1, encodingDeflate
|
||||
case zi == -1:
|
||||
return gi != -1, encodingGzip
|
||||
case gi < zi:
|
||||
return true, encodingGzip
|
||||
default:
|
||||
return true, encodingDeflate
|
||||
}
|
||||
}
|
||||
|
||||
type compressionResponseWriter struct {
|
||||
writer http.ResponseWriter
|
||||
compressor Compressor
|
||||
encoding string
|
||||
}
|
||||
|
||||
// NewCompressionResponseWriter returns wraps w with a compression ResponseWriter, using the given encoding
|
||||
func NewCompressionResponseWriter(w http.ResponseWriter, encoding string) (http.ResponseWriter, error) {
|
||||
var compressor Compressor
|
||||
switch encoding {
|
||||
case encodingGzip:
|
||||
compressor = gzip.NewWriter(w)
|
||||
case encodingDeflate:
|
||||
compressor = zlib.NewWriter(w)
|
||||
default:
|
||||
return nil, fmt.Errorf("%s is not a supported encoding type", encoding)
|
||||
}
|
||||
return &compressionResponseWriter{
|
||||
writer: w,
|
||||
compressor: compressor,
|
||||
encoding: encoding,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// compressionResponseWriter implements http.Responsewriter Interface
|
||||
var _ http.ResponseWriter = &compressionResponseWriter{}
|
||||
|
||||
func (c *compressionResponseWriter) Header() http.Header {
|
||||
return c.writer.Header()
|
||||
}
|
||||
|
||||
// compress data according to compression method
|
||||
func (c *compressionResponseWriter) Write(p []byte) (int, error) {
|
||||
if c.compressorClosed() {
|
||||
return -1, errors.New("compressing error: tried to write data using closed compressor")
|
||||
}
|
||||
c.Header().Set(headerContentEncoding, c.encoding)
|
||||
return c.compressor.Write(p)
|
||||
}
|
||||
|
||||
func (c *compressionResponseWriter) WriteHeader(status int) {
|
||||
c.writer.WriteHeader(status)
|
||||
}
|
||||
|
||||
// CloseNotify is part of http.CloseNotifier interface
|
||||
func (c *compressionResponseWriter) CloseNotify() <-chan bool {
|
||||
return c.writer.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
// Close the underlying compressor
|
||||
func (c *compressionResponseWriter) Close() error {
|
||||
if c.compressorClosed() {
|
||||
return errors.New("Compressing error: tried to close already closed compressor")
|
||||
}
|
||||
|
||||
c.compressor.Close()
|
||||
c.compressor = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *compressionResponseWriter) Flush() {
|
||||
if c.compressorClosed() {
|
||||
return
|
||||
}
|
||||
c.compressor.Flush()
|
||||
}
|
||||
|
||||
func (c *compressionResponseWriter) compressorClosed() bool {
|
||||
return nil == c.compressor
|
||||
}
|
||||
|
||||
// RestfulWithCompression wraps WithCompression to be compatible with go-restful
|
||||
func RestfulWithCompression(function restful.RouteFunction, ctxMapper request.RequestContextMapper) restful.RouteFunction {
|
||||
return restful.RouteFunction(func(request *restful.Request, response *restful.Response) {
|
||||
handler := WithCompression(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
response.ResponseWriter = w
|
||||
request.Request = req
|
||||
function(request, response)
|
||||
}), ctxMapper)
|
||||
handler.ServeHTTP(response.ResponseWriter, request.Request)
|
||||
})
|
||||
}
|
||||
110
vendor/k8s.io/apiserver/pkg/server/filters/compression_test.go
generated
vendored
Normal file
110
vendor/k8s.io/apiserver/pkg/server/filters/compression_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/endpoints/filters"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
func TestCompression(t *testing.T) {
|
||||
tests := []struct {
|
||||
encoding string
|
||||
watch bool
|
||||
}{
|
||||
{"", false},
|
||||
{"gzip", true},
|
||||
{"gzip", false},
|
||||
}
|
||||
|
||||
responseData := []byte("1234")
|
||||
|
||||
requestContextMapper := request.NewRequestContextMapper()
|
||||
|
||||
for _, test := range tests {
|
||||
handler := WithCompression(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
w.Write(responseData)
|
||||
}),
|
||||
requestContextMapper,
|
||||
)
|
||||
handler = filters.WithRequestInfo(handler, newTestRequestInfoResolver(), requestContextMapper)
|
||||
handler = request.WithRequestContext(handler, requestContextMapper)
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
client := http.Client{
|
||||
Transport: &http.Transport{
|
||||
DisableCompression: true,
|
||||
},
|
||||
}
|
||||
|
||||
url := server.URL + "/api/v1/pods"
|
||||
if test.watch {
|
||||
url = url + "?watch=1"
|
||||
}
|
||||
request, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
request.Header.Set("Accept-Encoding", test.encoding)
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
var reader io.Reader
|
||||
if test.encoding == "gzip" && !test.watch {
|
||||
if response.Header.Get("Content-Encoding") != "gzip" {
|
||||
t.Fatal("expected response header Content-Encoding to be set to \"gzip\"")
|
||||
}
|
||||
if response.Header.Get("Vary") != "Accept-Encoding" {
|
||||
t.Fatal("expected response header Vary to be set to \"Accept-Encoding\"")
|
||||
}
|
||||
reader, err = gzip.NewReader(response.Body)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
} else {
|
||||
if response.Header.Get("Content-Encoding") == "gzip" {
|
||||
t.Fatal("expected response header Content-Encoding not to be set")
|
||||
}
|
||||
reader = response.Body
|
||||
}
|
||||
body, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
t.Fatal("unexpected error: %v", err)
|
||||
}
|
||||
if !bytes.Equal(body, responseData) {
|
||||
t.Fatalf("Expected response body %s to equal %s", body, responseData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func newTestRequestInfoResolver() *request.RequestInfoFactory {
|
||||
return &request.RequestInfoFactory{
|
||||
APIPrefixes: sets.NewString("api", "apis"),
|
||||
GrouplessAPIPrefixes: sets.NewString("api"),
|
||||
}
|
||||
}
|
||||
98
vendor/k8s.io/apiserver/pkg/server/filters/cors.go
generated
vendored
Normal file
98
vendor/k8s.io/apiserver/pkg/server/filters/cors.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// TODO: use restful.CrossOriginResourceSharing
|
||||
// See github.com/emicklei/go-restful/blob/master/examples/restful-CORS-filter.go, and
|
||||
// github.com/emicklei/go-restful/blob/master/examples/restful-basic-authentication.go
|
||||
// Or, for a more detailed implementation use https://github.com/martini-contrib/cors
|
||||
// or implement CORS at your proxy layer.
|
||||
|
||||
// WithCORS is a simple CORS implementation that wraps an http Handler.
|
||||
// Pass nil for allowedMethods and allowedHeaders to use the defaults. If allowedOriginPatterns
|
||||
// is empty or nil, no CORS support is installed.
|
||||
func WithCORS(handler http.Handler, allowedOriginPatterns []string, allowedMethods []string, allowedHeaders []string, exposedHeaders []string, allowCredentials string) http.Handler {
|
||||
if len(allowedOriginPatterns) == 0 {
|
||||
return handler
|
||||
}
|
||||
allowedOriginPatternsREs := allowedOriginRegexps(allowedOriginPatterns)
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
origin := req.Header.Get("Origin")
|
||||
if origin != "" {
|
||||
allowed := false
|
||||
for _, re := range allowedOriginPatternsREs {
|
||||
if allowed = re.MatchString(origin); allowed {
|
||||
break
|
||||
}
|
||||
}
|
||||
if allowed {
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
// Set defaults for methods and headers if nothing was passed
|
||||
if allowedMethods == nil {
|
||||
allowedMethods = []string{"POST", "GET", "OPTIONS", "PUT", "DELETE", "PATCH"}
|
||||
}
|
||||
if allowedHeaders == nil {
|
||||
allowedHeaders = []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "X-Requested-With", "If-Modified-Since"}
|
||||
}
|
||||
if exposedHeaders == nil {
|
||||
exposedHeaders = []string{"Date"}
|
||||
}
|
||||
w.Header().Set("Access-Control-Allow-Methods", strings.Join(allowedMethods, ", "))
|
||||
w.Header().Set("Access-Control-Allow-Headers", strings.Join(allowedHeaders, ", "))
|
||||
w.Header().Set("Access-Control-Expose-Headers", strings.Join(exposedHeaders, ", "))
|
||||
w.Header().Set("Access-Control-Allow-Credentials", allowCredentials)
|
||||
|
||||
// Stop here if its a preflight OPTIONS request
|
||||
if req.Method == "OPTIONS" {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
// Dispatch to the next handler
|
||||
handler.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
|
||||
func allowedOriginRegexps(allowedOrigins []string) []*regexp.Regexp {
|
||||
res, err := compileRegexps(allowedOrigins)
|
||||
if err != nil {
|
||||
glog.Fatalf("Invalid CORS allowed origin, --cors-allowed-origins flag was set to %v - %v", strings.Join(allowedOrigins, ","), err)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Takes a list of strings and compiles them into a list of regular expressions
|
||||
func compileRegexps(regexpStrings []string) ([]*regexp.Regexp, error) {
|
||||
regexps := []*regexp.Regexp{}
|
||||
for _, regexpStr := range regexpStrings {
|
||||
r, err := regexp.Compile(regexpStr)
|
||||
if err != nil {
|
||||
return []*regexp.Regexp{}, err
|
||||
}
|
||||
regexps = append(regexps, r)
|
||||
}
|
||||
return regexps, nil
|
||||
}
|
||||
183
vendor/k8s.io/apiserver/pkg/server/filters/cors_test.go
generated
vendored
Normal file
183
vendor/k8s.io/apiserver/pkg/server/filters/cors_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCORSAllowedOrigins(t *testing.T) {
|
||||
table := []struct {
|
||||
allowedOrigins []string
|
||||
origin string
|
||||
allowed bool
|
||||
}{
|
||||
{[]string{}, "example.com", false},
|
||||
{[]string{"example.com"}, "example.com", true},
|
||||
{[]string{"example.com"}, "not-allowed.com", false},
|
||||
{[]string{"not-matching.com", "example.com"}, "example.com", true},
|
||||
{[]string{".*"}, "example.com", true},
|
||||
}
|
||||
|
||||
for _, item := range table {
|
||||
handler := WithCORS(
|
||||
http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}),
|
||||
item.allowedOrigins, nil, nil, nil, "true",
|
||||
)
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
client := http.Client{}
|
||||
|
||||
request, err := http.NewRequest("GET", server.URL+"/version", nil)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
request.Header.Set("Origin", item.origin)
|
||||
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if item.allowed {
|
||||
if !reflect.DeepEqual(item.origin, response.Header.Get("Access-Control-Allow-Origin")) {
|
||||
t.Errorf("Expected %#v, Got %#v", item.origin, response.Header.Get("Access-Control-Allow-Origin"))
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Allow-Credentials") == "" {
|
||||
t.Errorf("Expected Access-Control-Allow-Credentials header to be set")
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Allow-Headers") == "" {
|
||||
t.Errorf("Expected Access-Control-Allow-Headers header to be set")
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Allow-Methods") == "" {
|
||||
t.Errorf("Expected Access-Control-Allow-Methods header to be set")
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Expose-Headers") != "Date" {
|
||||
t.Errorf("Expected Date in Access-Control-Expose-Headers header")
|
||||
}
|
||||
} else {
|
||||
if response.Header.Get("Access-Control-Allow-Origin") != "" {
|
||||
t.Errorf("Expected Access-Control-Allow-Origin header to not be set")
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Allow-Credentials") != "" {
|
||||
t.Errorf("Expected Access-Control-Allow-Credentials header to not be set")
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Allow-Headers") != "" {
|
||||
t.Errorf("Expected Access-Control-Allow-Headers header to not be set")
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Allow-Methods") != "" {
|
||||
t.Errorf("Expected Access-Control-Allow-Methods header to not be set")
|
||||
}
|
||||
|
||||
if response.Header.Get("Access-Control-Expose-Headers") == "Date" {
|
||||
t.Errorf("Expected Date in Access-Control-Expose-Headers header")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCORSAllowedMethods(t *testing.T) {
|
||||
tests := []struct {
|
||||
allowedMethods []string
|
||||
method string
|
||||
allowed bool
|
||||
}{
|
||||
{nil, "POST", true},
|
||||
{nil, "GET", true},
|
||||
{nil, "OPTIONS", true},
|
||||
{nil, "PUT", true},
|
||||
{nil, "DELETE", true},
|
||||
{nil, "PATCH", true},
|
||||
{[]string{"GET", "POST"}, "PATCH", false},
|
||||
}
|
||||
|
||||
allowsMethod := func(res *http.Response, method string) bool {
|
||||
allowedMethods := strings.Split(res.Header.Get("Access-Control-Allow-Methods"), ",")
|
||||
for _, allowedMethod := range allowedMethods {
|
||||
if strings.TrimSpace(allowedMethod) == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
handler := WithCORS(
|
||||
http.HandlerFunc(func(http.ResponseWriter, *http.Request) {}),
|
||||
[]string{".*"}, test.allowedMethods, nil, nil, "true",
|
||||
)
|
||||
server := httptest.NewServer(handler)
|
||||
defer server.Close()
|
||||
client := http.Client{}
|
||||
|
||||
request, err := http.NewRequest(test.method, server.URL+"/version", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
request.Header.Set("Origin", "allowed.com")
|
||||
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
methodAllowed := allowsMethod(response, test.method)
|
||||
switch {
|
||||
case test.allowed && !methodAllowed:
|
||||
t.Errorf("Expected %v to be allowed, Got only %#v", test.method, response.Header.Get("Access-Control-Allow-Methods"))
|
||||
case !test.allowed && methodAllowed:
|
||||
t.Errorf("Unexpected allowed method %v, Expected only %#v", test.method, response.Header.Get("Access-Control-Allow-Methods"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestCompileRegex(t *testing.T) {
|
||||
uncompiledRegexes := []string{"endsWithMe$", "^startingWithMe"}
|
||||
regexes, err := compileRegexps(uncompiledRegexes)
|
||||
|
||||
if err != nil {
|
||||
t.Errorf("Failed to compile legal regexes: '%v': %v", uncompiledRegexes, err)
|
||||
}
|
||||
if len(regexes) != len(uncompiledRegexes) {
|
||||
t.Errorf("Wrong number of regexes returned: '%v': %v", uncompiledRegexes, regexes)
|
||||
}
|
||||
|
||||
if !regexes[0].MatchString("Something that endsWithMe") {
|
||||
t.Errorf("Wrong regex returned: '%v': %v", uncompiledRegexes[0], regexes[0])
|
||||
}
|
||||
if regexes[0].MatchString("Something that doesn't endsWithMe.") {
|
||||
t.Errorf("Wrong regex returned: '%v': %v", uncompiledRegexes[0], regexes[0])
|
||||
}
|
||||
if !regexes[1].MatchString("startingWithMe is very important") {
|
||||
t.Errorf("Wrong regex returned: '%v': %v", uncompiledRegexes[1], regexes[1])
|
||||
}
|
||||
if regexes[1].MatchString("not startingWithMe should fail") {
|
||||
t.Errorf("Wrong regex returned: '%v': %v", uncompiledRegexes[1], regexes[1])
|
||||
}
|
||||
}
|
||||
19
vendor/k8s.io/apiserver/pkg/server/filters/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/apiserver/pkg/server/filters/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
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 filters contains all the http handler chain filters which
|
||||
// are not api related.
|
||||
package filters // import "k8s.io/apiserver/pkg/server/filters"
|
||||
37
vendor/k8s.io/apiserver/pkg/server/filters/longrunning.go
generated
vendored
Normal file
37
vendor/k8s.io/apiserver/pkg/server/filters/longrunning.go
generated
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
// BasicLongRunningRequestCheck returns true if the given request has one of the specified verbs or one of the specified subresources
|
||||
func BasicLongRunningRequestCheck(longRunningVerbs, longRunningSubresources sets.String) apirequest.LongRunningRequestCheck {
|
||||
return func(r *http.Request, requestInfo *apirequest.RequestInfo) bool {
|
||||
if longRunningVerbs.Has(requestInfo.Verb) {
|
||||
return true
|
||||
}
|
||||
if requestInfo.IsResourceRequest && longRunningSubresources.Has(requestInfo.Subresource) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
133
vendor/k8s.io/apiserver/pkg/server/filters/maxinflight.go
generated
vendored
Normal file
133
vendor/k8s.io/apiserver/pkg/server/filters/maxinflight.go
generated
vendored
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// Constant for the retry-after interval on rate limiting.
|
||||
// TODO: maybe make this dynamic? or user-adjustable?
|
||||
const retryAfter = "1"
|
||||
|
||||
var nonMutatingRequestVerbs = sets.NewString("get", "list", "watch")
|
||||
|
||||
func handleError(w http.ResponseWriter, r *http.Request, err error) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
fmt.Fprintf(w, "Internal Server Error: %#v", r.RequestURI)
|
||||
glog.Errorf(err.Error())
|
||||
}
|
||||
|
||||
// WithMaxInFlightLimit limits the number of in-flight requests to buffer size of the passed in channel.
|
||||
func WithMaxInFlightLimit(
|
||||
handler http.Handler,
|
||||
nonMutatingLimit int,
|
||||
mutatingLimit int,
|
||||
requestContextMapper genericapirequest.RequestContextMapper,
|
||||
longRunningRequestCheck apirequest.LongRunningRequestCheck,
|
||||
) http.Handler {
|
||||
if nonMutatingLimit == 0 && mutatingLimit == 0 {
|
||||
return handler
|
||||
}
|
||||
var nonMutatingChan chan bool
|
||||
var mutatingChan chan bool
|
||||
if nonMutatingLimit != 0 {
|
||||
nonMutatingChan = make(chan bool, nonMutatingLimit)
|
||||
}
|
||||
if mutatingLimit != 0 {
|
||||
mutatingChan = make(chan bool, mutatingLimit)
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, ok := requestContextMapper.Get(r)
|
||||
if !ok {
|
||||
handleError(w, r, fmt.Errorf("no context found for request, handler chain must be wrong"))
|
||||
return
|
||||
}
|
||||
requestInfo, ok := apirequest.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
handleError(w, r, fmt.Errorf("no RequestInfo found in context, handler chain must be wrong"))
|
||||
return
|
||||
}
|
||||
|
||||
// Skip tracking long running events.
|
||||
if longRunningRequestCheck != nil && longRunningRequestCheck(r, requestInfo) {
|
||||
handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var c chan bool
|
||||
if !nonMutatingRequestVerbs.Has(requestInfo.Verb) {
|
||||
c = mutatingChan
|
||||
} else {
|
||||
c = nonMutatingChan
|
||||
}
|
||||
|
||||
if c == nil {
|
||||
handler.ServeHTTP(w, r)
|
||||
} else {
|
||||
|
||||
select {
|
||||
case c <- true:
|
||||
defer func() { <-c }()
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
default:
|
||||
// at this point we're about to return a 429, BUT not all actors should be rate limited. A system:master is so powerful
|
||||
// that he should always get an answer. It's a super-admin or a loopback connection.
|
||||
if currUser, ok := apirequest.UserFrom(ctx); ok {
|
||||
for _, group := range currUser.GetGroups() {
|
||||
if group == user.SystemPrivilegedGroup {
|
||||
handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
scope := "cluster"
|
||||
if requestInfo.Namespace != "" {
|
||||
scope = "namespace"
|
||||
}
|
||||
if requestInfo.Name != "" {
|
||||
scope = "resource"
|
||||
}
|
||||
if requestInfo.IsResourceRequest {
|
||||
metrics.MonitorRequest(r, strings.ToUpper(requestInfo.Verb), requestInfo.Resource, requestInfo.Subresource, "", scope, http.StatusTooManyRequests, 0, time.Now())
|
||||
} else {
|
||||
metrics.MonitorRequest(r, strings.ToUpper(requestInfo.Verb), "", requestInfo.Path, "", scope, http.StatusTooManyRequests, 0, time.Now())
|
||||
}
|
||||
tooManyRequests(r, w)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func tooManyRequests(req *http.Request, w http.ResponseWriter) {
|
||||
// Return a 429 status indicating "Too Many Requests"
|
||||
w.Header().Set("Retry-After", retryAfter)
|
||||
http.Error(w, "Too many requests, please try again later.", http.StatusTooManyRequests)
|
||||
}
|
||||
316
vendor/k8s.io/apiserver/pkg/server/filters/maxinflight_test.go
generated
vendored
Normal file
316
vendor/k8s.io/apiserver/pkg/server/filters/maxinflight_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
apifilters "k8s.io/apiserver/pkg/endpoints/filters"
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
func createMaxInflightServer(callsWg, blockWg *sync.WaitGroup, disableCallsWg *bool, disableCallsWgMutex *sync.Mutex, nonMutating, mutating int) *httptest.Server {
|
||||
longRunningRequestCheck := BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString("proxy"))
|
||||
|
||||
requestContextMapper := apirequest.NewRequestContextMapper()
|
||||
requestInfoFactory := &apirequest.RequestInfoFactory{APIPrefixes: sets.NewString("apis", "api"), GrouplessAPIPrefixes: sets.NewString("api")}
|
||||
handler := WithMaxInFlightLimit(
|
||||
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// A short, accounted request that does not wait for block WaitGroup.
|
||||
if strings.Contains(r.URL.Path, "dontwait") {
|
||||
return
|
||||
}
|
||||
disableCallsWgMutex.Lock()
|
||||
waitForCalls := *disableCallsWg
|
||||
disableCallsWgMutex.Unlock()
|
||||
if waitForCalls {
|
||||
callsWg.Done()
|
||||
}
|
||||
blockWg.Wait()
|
||||
}),
|
||||
nonMutating,
|
||||
mutating,
|
||||
requestContextMapper,
|
||||
longRunningRequestCheck,
|
||||
)
|
||||
handler = withFakeUser(handler, requestContextMapper)
|
||||
handler = apifilters.WithRequestInfo(handler, requestInfoFactory, requestContextMapper)
|
||||
handler = apirequest.WithRequestContext(handler, requestContextMapper)
|
||||
|
||||
return httptest.NewServer(handler)
|
||||
}
|
||||
|
||||
func withFakeUser(handler http.Handler, requestContextMapper apirequest.RequestContextMapper) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, ok := requestContextMapper.Get(r)
|
||||
if !ok {
|
||||
handleError(w, r, fmt.Errorf("no context found for request, handler chain must be wrong"))
|
||||
return
|
||||
}
|
||||
|
||||
if len(r.Header["Groups"]) > 0 {
|
||||
requestContextMapper.Update(r, apirequest.WithUser(ctx, &user.DefaultInfo{
|
||||
Groups: r.Header["Groups"],
|
||||
}))
|
||||
}
|
||||
handler.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// Tests that MaxInFlightLimit works, i.e.
|
||||
// - "long" requests such as proxy or watch, identified by regexp are not accounted despite
|
||||
// hanging for the long time,
|
||||
// - "short" requests are correctly accounted, i.e. there can be only size of channel passed to the
|
||||
// constructor in flight at any given moment,
|
||||
// - subsequent "short" requests are rejected instantly with appropriate error,
|
||||
// - subsequent "long" requests are handled normally,
|
||||
// - we correctly recover after some "short" requests finish, i.e. we can process new ones.
|
||||
func TestMaxInFlightNonMutating(t *testing.T) {
|
||||
const AllowedNonMutatingInflightRequestsNo = 3
|
||||
|
||||
// Calls is used to wait until all server calls are received. We are sending
|
||||
// AllowedNonMutatingInflightRequestsNo of 'long' not-accounted requests and the same number of
|
||||
// 'short' accounted ones.
|
||||
calls := &sync.WaitGroup{}
|
||||
calls.Add(AllowedNonMutatingInflightRequestsNo * 2)
|
||||
|
||||
// Responses is used to wait until all responses are
|
||||
// received. This prevents some async requests getting EOF
|
||||
// errors from prematurely closing the server
|
||||
responses := &sync.WaitGroup{}
|
||||
responses.Add(AllowedNonMutatingInflightRequestsNo * 2)
|
||||
|
||||
// Block is used to keep requests in flight for as long as we need to. All requests will
|
||||
// be unblocked at the same time.
|
||||
block := &sync.WaitGroup{}
|
||||
block.Add(1)
|
||||
|
||||
waitForCalls := true
|
||||
waitForCallsMutex := sync.Mutex{}
|
||||
|
||||
server := createMaxInflightServer(calls, block, &waitForCalls, &waitForCallsMutex, AllowedNonMutatingInflightRequestsNo, 1)
|
||||
defer server.Close()
|
||||
|
||||
// These should hang, but not affect accounting. use a query param match
|
||||
for i := 0; i < AllowedNonMutatingInflightRequestsNo; i++ {
|
||||
// These should hang waiting on block...
|
||||
go func() {
|
||||
if err := expectHTTPGet(server.URL+"/api/v1/namespaces/default/wait?watch=true", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
responses.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// Check that sever is not saturated by not-accounted calls
|
||||
if err := expectHTTPGet(server.URL+"/dontwait", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// These should hang and be accounted, i.e. saturate the server
|
||||
for i := 0; i < AllowedNonMutatingInflightRequestsNo; i++ {
|
||||
// These should hang waiting on block...
|
||||
go func() {
|
||||
if err := expectHTTPGet(server.URL, http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
responses.Done()
|
||||
}()
|
||||
}
|
||||
// We wait for all calls to be received by the server
|
||||
calls.Wait()
|
||||
// Disable calls notifications in the server
|
||||
waitForCallsMutex.Lock()
|
||||
waitForCalls = false
|
||||
waitForCallsMutex.Unlock()
|
||||
|
||||
// Do this multiple times to show that rate limit rejected requests don't block.
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := expectHTTPGet(server.URL, http.StatusTooManyRequests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
// Validate that non-accounted URLs still work. use a path regex match
|
||||
if err := expectHTTPGet(server.URL+"/api/v1/watch/namespaces/default/dontwait", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// We should allow a single mutating request.
|
||||
if err := expectHTTPPost(server.URL+"/dontwait", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Let all hanging requests finish
|
||||
block.Done()
|
||||
|
||||
// Show that we recover from being blocked up.
|
||||
// Too avoid flakyness we need to wait until at least one of the requests really finishes.
|
||||
responses.Wait()
|
||||
if err := expectHTTPGet(server.URL, http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxInFlightMutating(t *testing.T) {
|
||||
const AllowedMutatingInflightRequestsNo = 3
|
||||
|
||||
calls := &sync.WaitGroup{}
|
||||
calls.Add(AllowedMutatingInflightRequestsNo)
|
||||
|
||||
responses := &sync.WaitGroup{}
|
||||
responses.Add(AllowedMutatingInflightRequestsNo)
|
||||
|
||||
// Block is used to keep requests in flight for as long as we need to. All requests will
|
||||
// be unblocked at the same time.
|
||||
block := &sync.WaitGroup{}
|
||||
block.Add(1)
|
||||
|
||||
waitForCalls := true
|
||||
waitForCallsMutex := sync.Mutex{}
|
||||
|
||||
server := createMaxInflightServer(calls, block, &waitForCalls, &waitForCallsMutex, 1, AllowedMutatingInflightRequestsNo)
|
||||
defer server.Close()
|
||||
|
||||
// These should hang and be accounted, i.e. saturate the server
|
||||
for i := 0; i < AllowedMutatingInflightRequestsNo; i++ {
|
||||
// These should hang waiting on block...
|
||||
go func() {
|
||||
if err := expectHTTPPost(server.URL+"/foo/bar", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
responses.Done()
|
||||
}()
|
||||
}
|
||||
// We wait for all calls to be received by the server
|
||||
calls.Wait()
|
||||
// Disable calls notifications in the server
|
||||
// Disable calls notifications in the server
|
||||
waitForCallsMutex.Lock()
|
||||
waitForCalls = false
|
||||
waitForCallsMutex.Unlock()
|
||||
|
||||
// Do this multiple times to show that rate limit rejected requests don't block.
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := expectHTTPPost(server.URL+"/foo/bar/", http.StatusTooManyRequests); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
// Validate that non-mutating URLs still work. use a path regex match
|
||||
if err := expectHTTPGet(server.URL+"/dontwait", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Let all hanging requests finish
|
||||
block.Done()
|
||||
|
||||
// Show that we recover from being blocked up.
|
||||
// Too avoid flakyness we need to wait until at least one of the requests really finishes.
|
||||
responses.Wait()
|
||||
if err := expectHTTPPost(server.URL+"/foo/bar", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// We use GET as a sample non-mutating request.
|
||||
func expectHTTPGet(url string, code int) error {
|
||||
r, err := http.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if r.StatusCode != code {
|
||||
return fmt.Errorf("unexpected response: %v", r.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// We use POST as a sample mutating request.
|
||||
func expectHTTPPost(url string, code int, groups ...string) error {
|
||||
req, err := http.NewRequest(http.MethodPost, url, strings.NewReader("foo bar"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, group := range groups {
|
||||
req.Header.Add("Groups", group)
|
||||
}
|
||||
|
||||
r, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
if r.StatusCode != code {
|
||||
return fmt.Errorf("unexpected response: %v", r.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestMaxInFlightSkipsMasters(t *testing.T) {
|
||||
const AllowedMutatingInflightRequestsNo = 3
|
||||
|
||||
calls := &sync.WaitGroup{}
|
||||
calls.Add(AllowedMutatingInflightRequestsNo)
|
||||
|
||||
responses := &sync.WaitGroup{}
|
||||
responses.Add(AllowedMutatingInflightRequestsNo)
|
||||
|
||||
// Block is used to keep requests in flight for as long as we need to. All requests will
|
||||
// be unblocked at the same time.
|
||||
block := &sync.WaitGroup{}
|
||||
block.Add(1)
|
||||
|
||||
waitForCalls := true
|
||||
waitForCallsMutex := sync.Mutex{}
|
||||
|
||||
server := createMaxInflightServer(calls, block, &waitForCalls, &waitForCallsMutex, 1, AllowedMutatingInflightRequestsNo)
|
||||
defer server.Close()
|
||||
|
||||
// These should hang and be accounted, i.e. saturate the server
|
||||
for i := 0; i < AllowedMutatingInflightRequestsNo; i++ {
|
||||
// These should hang waiting on block...
|
||||
go func() {
|
||||
if err := expectHTTPPost(server.URL+"/foo/bar", http.StatusOK); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
responses.Done()
|
||||
}()
|
||||
}
|
||||
// We wait for all calls to be received by the server
|
||||
calls.Wait()
|
||||
// Disable calls notifications in the server
|
||||
// Disable calls notifications in the server
|
||||
waitForCallsMutex.Lock()
|
||||
waitForCalls = false
|
||||
waitForCallsMutex.Unlock()
|
||||
|
||||
// Do this multiple times to show that rate limit rejected requests don't block.
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := expectHTTPPost(server.URL+"/dontwait", http.StatusOK, user.SystemPrivilegedGroup); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Let all hanging requests finish
|
||||
block.Done()
|
||||
|
||||
responses.Wait()
|
||||
}
|
||||
287
vendor/k8s.io/apiserver/pkg/server/filters/timeout.go
generated
vendored
Normal file
287
vendor/k8s.io/apiserver/pkg/server/filters/timeout.go
generated
vendored
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
)
|
||||
|
||||
var errConnKilled = fmt.Errorf("kill connection/stream")
|
||||
|
||||
// WithTimeoutForNonLongRunningRequests times out non-long-running requests after the time given by timeout.
|
||||
func WithTimeoutForNonLongRunningRequests(handler http.Handler, requestContextMapper apirequest.RequestContextMapper, longRunning apirequest.LongRunningRequestCheck, timeout time.Duration) http.Handler {
|
||||
if longRunning == nil {
|
||||
return handler
|
||||
}
|
||||
timeoutFunc := func(req *http.Request) (<-chan time.Time, func(), *apierrors.StatusError) {
|
||||
// TODO unify this with apiserver.MaxInFlightLimit
|
||||
ctx, ok := requestContextMapper.Get(req)
|
||||
if !ok {
|
||||
// if this happens, the handler chain isn't setup correctly because there is no context mapper
|
||||
return time.After(timeout), func() {}, apierrors.NewInternalError(fmt.Errorf("no context found for request during timeout"))
|
||||
}
|
||||
|
||||
requestInfo, ok := apirequest.RequestInfoFrom(ctx)
|
||||
if !ok {
|
||||
// if this happens, the handler chain isn't setup correctly because there is no request info
|
||||
return time.After(timeout), func() {}, apierrors.NewInternalError(fmt.Errorf("no request info found for request during timeout"))
|
||||
}
|
||||
|
||||
if longRunning(req, requestInfo) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
now := time.Now()
|
||||
metricFn := func() {
|
||||
scope := "cluster"
|
||||
if requestInfo.Namespace != "" {
|
||||
scope = "namespace"
|
||||
}
|
||||
if requestInfo.Name != "" {
|
||||
scope = "resource"
|
||||
}
|
||||
if requestInfo.IsResourceRequest {
|
||||
metrics.MonitorRequest(req, strings.ToUpper(requestInfo.Verb), requestInfo.Resource, requestInfo.Subresource, "", scope, http.StatusGatewayTimeout, 0, now)
|
||||
} else {
|
||||
metrics.MonitorRequest(req, strings.ToUpper(requestInfo.Verb), "", requestInfo.Path, "", scope, http.StatusGatewayTimeout, 0, now)
|
||||
}
|
||||
}
|
||||
return time.After(timeout), metricFn, apierrors.NewTimeoutError(fmt.Sprintf("request did not complete within %s", timeout), 0)
|
||||
}
|
||||
return WithTimeout(handler, timeoutFunc)
|
||||
}
|
||||
|
||||
// WithTimeout returns an http.Handler that runs h with a timeout
|
||||
// determined by timeoutFunc. The new http.Handler calls h.ServeHTTP to handle
|
||||
// each request, but if a call runs for longer than its time limit, the
|
||||
// handler responds with a 504 Gateway Timeout error and the message
|
||||
// provided. (If msg is empty, a suitable default message will be sent.) After
|
||||
// the handler times out, writes by h to its http.ResponseWriter will return
|
||||
// http.ErrHandlerTimeout. If timeoutFunc returns a nil timeout channel, no
|
||||
// timeout will be enforced. recordFn is a function that will be invoked whenever
|
||||
// a timeout happens.
|
||||
func WithTimeout(h http.Handler, timeoutFunc func(*http.Request) (timeout <-chan time.Time, recordFn func(), err *apierrors.StatusError)) http.Handler {
|
||||
return &timeoutHandler{h, timeoutFunc}
|
||||
}
|
||||
|
||||
type timeoutHandler struct {
|
||||
handler http.Handler
|
||||
timeout func(*http.Request) (<-chan time.Time, func(), *apierrors.StatusError)
|
||||
}
|
||||
|
||||
func (t *timeoutHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
after, recordFn, err := t.timeout(r)
|
||||
if after == nil {
|
||||
t.handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
tw := newTimeoutWriter(w)
|
||||
go func() {
|
||||
t.handler.ServeHTTP(tw, r)
|
||||
close(done)
|
||||
}()
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-after:
|
||||
recordFn()
|
||||
tw.timeout(err)
|
||||
}
|
||||
}
|
||||
|
||||
type timeoutWriter interface {
|
||||
http.ResponseWriter
|
||||
timeout(*apierrors.StatusError)
|
||||
}
|
||||
|
||||
func newTimeoutWriter(w http.ResponseWriter) timeoutWriter {
|
||||
base := &baseTimeoutWriter{w: w}
|
||||
|
||||
_, notifiable := w.(http.CloseNotifier)
|
||||
_, hijackable := w.(http.Hijacker)
|
||||
|
||||
switch {
|
||||
case notifiable && hijackable:
|
||||
return &closeHijackTimeoutWriter{base}
|
||||
case notifiable:
|
||||
return &closeTimeoutWriter{base}
|
||||
case hijackable:
|
||||
return &hijackTimeoutWriter{base}
|
||||
default:
|
||||
return base
|
||||
}
|
||||
}
|
||||
|
||||
type baseTimeoutWriter struct {
|
||||
w http.ResponseWriter
|
||||
|
||||
mu sync.Mutex
|
||||
// if the timeout handler has timedout
|
||||
timedOut bool
|
||||
// if this timeout writer has wrote header
|
||||
wroteHeader bool
|
||||
// if this timeout writer has been hijacked
|
||||
hijacked bool
|
||||
}
|
||||
|
||||
func (tw *baseTimeoutWriter) Header() http.Header {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
if tw.timedOut {
|
||||
return http.Header{}
|
||||
}
|
||||
|
||||
return tw.w.Header()
|
||||
}
|
||||
|
||||
func (tw *baseTimeoutWriter) Write(p []byte) (int, error) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
if tw.timedOut {
|
||||
return 0, http.ErrHandlerTimeout
|
||||
}
|
||||
if tw.hijacked {
|
||||
return 0, http.ErrHijacked
|
||||
}
|
||||
|
||||
tw.wroteHeader = true
|
||||
return tw.w.Write(p)
|
||||
}
|
||||
|
||||
func (tw *baseTimeoutWriter) Flush() {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
if tw.timedOut {
|
||||
return
|
||||
}
|
||||
|
||||
if flusher, ok := tw.w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *baseTimeoutWriter) WriteHeader(code int) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
if tw.timedOut || tw.wroteHeader || tw.hijacked {
|
||||
return
|
||||
}
|
||||
|
||||
tw.wroteHeader = true
|
||||
tw.w.WriteHeader(code)
|
||||
}
|
||||
|
||||
func (tw *baseTimeoutWriter) timeout(err *apierrors.StatusError) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
tw.timedOut = true
|
||||
|
||||
// The timeout writer has not been used by the inner handler.
|
||||
// We can safely timeout the HTTP request by sending by a timeout
|
||||
// handler
|
||||
if !tw.wroteHeader && !tw.hijacked {
|
||||
tw.w.WriteHeader(http.StatusGatewayTimeout)
|
||||
enc := json.NewEncoder(tw.w)
|
||||
enc.Encode(&err.ErrStatus)
|
||||
} else {
|
||||
// The timeout writer has been used by the inner handler. There is
|
||||
// no way to timeout the HTTP request at the point. We have to shutdown
|
||||
// the connection for HTTP1 or reset stream for HTTP2.
|
||||
//
|
||||
// Note from: Brad Fitzpatrick
|
||||
// if the ServeHTTP goroutine panics, that will do the best possible thing for both
|
||||
// HTTP/1 and HTTP/2. In HTTP/1, assuming you're replying with at least HTTP/1.1 and
|
||||
// you've already flushed the headers so it's using HTTP chunking, it'll kill the TCP
|
||||
// connection immediately without a proper 0-byte EOF chunk, so the peer will recognize
|
||||
// the response as bogus. In HTTP/2 the server will just RST_STREAM the stream, leaving
|
||||
// the TCP connection open, but resetting the stream to the peer so it'll have an error,
|
||||
// like the HTTP/1 case.
|
||||
panic(errConnKilled)
|
||||
}
|
||||
}
|
||||
|
||||
func (tw *baseTimeoutWriter) closeNotify() <-chan bool {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
if tw.timedOut {
|
||||
done := make(chan bool)
|
||||
close(done)
|
||||
return done
|
||||
}
|
||||
|
||||
return tw.w.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func (tw *baseTimeoutWriter) hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
tw.mu.Lock()
|
||||
defer tw.mu.Unlock()
|
||||
|
||||
if tw.timedOut {
|
||||
return nil, nil, http.ErrHandlerTimeout
|
||||
}
|
||||
conn, rw, err := tw.w.(http.Hijacker).Hijack()
|
||||
if err == nil {
|
||||
tw.hijacked = true
|
||||
}
|
||||
return conn, rw, err
|
||||
}
|
||||
|
||||
type closeTimeoutWriter struct {
|
||||
*baseTimeoutWriter
|
||||
}
|
||||
|
||||
func (tw *closeTimeoutWriter) CloseNotify() <-chan bool {
|
||||
return tw.closeNotify()
|
||||
}
|
||||
|
||||
type hijackTimeoutWriter struct {
|
||||
*baseTimeoutWriter
|
||||
}
|
||||
|
||||
func (tw *hijackTimeoutWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return tw.hijack()
|
||||
}
|
||||
|
||||
type closeHijackTimeoutWriter struct {
|
||||
*baseTimeoutWriter
|
||||
}
|
||||
|
||||
func (tw *closeHijackTimeoutWriter) CloseNotify() <-chan bool {
|
||||
return tw.closeNotify()
|
||||
}
|
||||
|
||||
func (tw *closeHijackTimeoutWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
return tw.hijack()
|
||||
}
|
||||
117
vendor/k8s.io/apiserver/pkg/server/filters/timeout_test.go
generated
vendored
Normal file
117
vendor/k8s.io/apiserver/pkg/server/filters/timeout_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
)
|
||||
|
||||
type recorder struct {
|
||||
lock sync.Mutex
|
||||
count int
|
||||
}
|
||||
|
||||
func (r *recorder) Record() {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
r.count++
|
||||
}
|
||||
|
||||
func (r *recorder) Count() int {
|
||||
r.lock.Lock()
|
||||
defer r.lock.Unlock()
|
||||
return r.count
|
||||
}
|
||||
|
||||
func TestTimeout(t *testing.T) {
|
||||
sendResponse := make(chan struct{}, 1)
|
||||
writeErrors := make(chan error, 1)
|
||||
timeout := make(chan time.Time, 1)
|
||||
resp := "test response"
|
||||
timeoutErr := apierrors.NewServerTimeout(schema.GroupResource{Group: "foo", Resource: "bar"}, "get", 0)
|
||||
record := &recorder{}
|
||||
|
||||
ts := httptest.NewServer(WithTimeout(http.HandlerFunc(
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
<-sendResponse
|
||||
_, err := w.Write([]byte(resp))
|
||||
writeErrors <- err
|
||||
}),
|
||||
func(*http.Request) (<-chan time.Time, func(), *apierrors.StatusError) {
|
||||
return timeout, record.Record, timeoutErr
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
// No timeouts
|
||||
sendResponse <- struct{}{}
|
||||
res, err := http.Get(ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.StatusCode != http.StatusOK {
|
||||
t.Errorf("got res.StatusCode %d; expected %d", res.StatusCode, http.StatusOK)
|
||||
}
|
||||
body, _ := ioutil.ReadAll(res.Body)
|
||||
if string(body) != resp {
|
||||
t.Errorf("got body %q; expected %q", string(body), resp)
|
||||
}
|
||||
if err := <-writeErrors; err != nil {
|
||||
t.Errorf("got unexpected Write error on first request: %v", err)
|
||||
}
|
||||
if record.Count() != 0 {
|
||||
t.Errorf("invoked record method: %#v", record)
|
||||
}
|
||||
|
||||
// Times out
|
||||
timeout <- time.Time{}
|
||||
res, err = http.Get(ts.URL)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if res.StatusCode != http.StatusGatewayTimeout {
|
||||
t.Errorf("got res.StatusCode %d; expected %d", res.StatusCode, http.StatusServiceUnavailable)
|
||||
}
|
||||
body, _ = ioutil.ReadAll(res.Body)
|
||||
status := &metav1.Status{}
|
||||
if err := json.Unmarshal(body, status); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(status, &timeoutErr.ErrStatus) {
|
||||
t.Errorf("unexpected object: %s", diff.ObjectReflectDiff(&timeoutErr.ErrStatus, status))
|
||||
}
|
||||
if record.Count() != 1 {
|
||||
t.Errorf("did not invoke record method: %#v", record)
|
||||
}
|
||||
|
||||
// Now try to send a response
|
||||
sendResponse <- struct{}{}
|
||||
if err := <-writeErrors; err != http.ErrHandlerTimeout {
|
||||
t.Errorf("got Write error of %v; expected %v", err, http.ErrHandlerTimeout)
|
||||
}
|
||||
}
|
||||
43
vendor/k8s.io/apiserver/pkg/server/filters/wrap.go
generated
vendored
Normal file
43
vendor/k8s.io/apiserver/pkg/server/filters/wrap.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
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 filters
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apiserver/pkg/server/httplog"
|
||||
)
|
||||
|
||||
// WithPanicRecovery wraps an http Handler to recover and log panics.
|
||||
func WithPanicRecovery(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
defer runtime.HandleCrash(func(err interface{}) {
|
||||
http.Error(w, "This request caused apisever to panic. Look in log for details.", http.StatusInternalServerError)
|
||||
glog.Errorf("APIServer panic'd on %v %v: %v\n%s\n", req.Method, req.RequestURI, err, debug.Stack())
|
||||
})
|
||||
|
||||
logger := httplog.NewLogged(req, &w)
|
||||
defer logger.Log()
|
||||
|
||||
// Dispatch to the internal handler
|
||||
handler.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
447
vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
generated
vendored
Normal file
447
vendor/k8s.io/apiserver/pkg/server/genericapiserver.go
generated
vendored
Normal file
|
|
@ -0,0 +1,447 @@
|
|||
/*
|
||||
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"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
systemd "github.com/coreos/go-systemd/daemon"
|
||||
"github.com/emicklei/go-restful-swagger12"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apimachinery"
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
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"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
genericapi "k8s.io/apiserver/pkg/endpoints"
|
||||
"k8s.io/apiserver/pkg/endpoints/discovery"
|
||||
apirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
"k8s.io/apiserver/pkg/server/routes"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
openapicommon "k8s.io/kube-openapi/pkg/common"
|
||||
)
|
||||
|
||||
// Info about an API group.
|
||||
type APIGroupInfo struct {
|
||||
GroupMeta apimachinery.GroupMeta
|
||||
// Info about the resources in this group. Its 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
|
||||
|
||||
// SubresourceGroupVersionKind contains the GroupVersionKind overrides for each subresource that is
|
||||
// accessible from this API group version. The GroupVersionKind is that of the external version of
|
||||
// the subresource. The key of this map should be the path of the subresource. The keys here should
|
||||
// match the keys in the Storage map above for subresources.
|
||||
SubresourceGroupVersionKind map[string]schema.GroupVersionKind
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// requestContextMapper provides a way to get the context for a request. It may be nil.
|
||||
requestContextMapper apirequest.RequestContextMapper
|
||||
|
||||
SecureServingInfo *SecureServingInfo
|
||||
|
||||
// numerical ports, set after listening
|
||||
effectiveSecurePort int
|
||||
|
||||
// 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.
|
||||
swaggerConfig *swagger.Config
|
||||
openAPIConfig *openapicommon.Config
|
||||
|
||||
// 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
|
||||
|
||||
// healthz checks
|
||||
healthzLock sync.Mutex
|
||||
healthzChecks []healthz.HealthzChecker
|
||||
healthzCreated bool
|
||||
|
||||
// auditing. The backend is started after the server starts listening.
|
||||
AuditBackend audit.Backend
|
||||
|
||||
// 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 or nil
|
||||
delegationTarget DelegationTarget
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// RequestContextMapper returns the existing RequestContextMapper. Because we cannot rewire all existing
|
||||
// uses of this function, this will be used in any delegating API server
|
||||
RequestContextMapper() apirequest.RequestContextMapper
|
||||
|
||||
// PostStartHooks returns the post-start hooks that need to be combined
|
||||
PostStartHooks() map[string]postStartHookEntry
|
||||
|
||||
// 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) HealthzChecks() []healthz.HealthzChecker {
|
||||
return s.healthzChecks
|
||||
}
|
||||
func (s *GenericAPIServer) ListedPaths() []string {
|
||||
return s.listedPathProvider.ListedPaths()
|
||||
}
|
||||
|
||||
func (s *GenericAPIServer) NextDelegate() DelegationTarget {
|
||||
return s.delegationTarget
|
||||
}
|
||||
|
||||
var EmptyDelegate = emptyDelegate{
|
||||
requestContextMapper: apirequest.NewRequestContextMapper(),
|
||||
}
|
||||
|
||||
type emptyDelegate struct {
|
||||
requestContextMapper apirequest.RequestContextMapper
|
||||
}
|
||||
|
||||
func (s emptyDelegate) UnprotectedHandler() http.Handler {
|
||||
return nil
|
||||
}
|
||||
func (s emptyDelegate) PostStartHooks() map[string]postStartHookEntry {
|
||||
return map[string]postStartHookEntry{}
|
||||
}
|
||||
func (s emptyDelegate) HealthzChecks() []healthz.HealthzChecker {
|
||||
return []healthz.HealthzChecker{}
|
||||
}
|
||||
func (s emptyDelegate) ListedPaths() []string {
|
||||
return []string{}
|
||||
}
|
||||
func (s emptyDelegate) RequestContextMapper() apirequest.RequestContextMapper {
|
||||
return s.requestContextMapper
|
||||
}
|
||||
func (s emptyDelegate) NextDelegate() DelegationTarget {
|
||||
return nil
|
||||
}
|
||||
|
||||
// RequestContextMapper is exposed so that third party resource storage can be build in a different location.
|
||||
// TODO refactor third party resource storage
|
||||
func (s *GenericAPIServer) RequestContextMapper() apirequest.RequestContextMapper {
|
||||
return s.requestContextMapper
|
||||
}
|
||||
|
||||
// MinRequestTimeout is exposed so that third party resource storage can be build in a different location.
|
||||
// TODO refactor third party resource storage
|
||||
func (s *GenericAPIServer) MinRequestTimeout() time.Duration {
|
||||
return s.minRequestTimeout
|
||||
}
|
||||
|
||||
type preparedGenericAPIServer struct {
|
||||
*GenericAPIServer
|
||||
}
|
||||
|
||||
// PrepareRun does post API installation setup steps.
|
||||
func (s *GenericAPIServer) PrepareRun() preparedGenericAPIServer {
|
||||
if s.swaggerConfig != nil {
|
||||
routes.Swagger{Config: s.swaggerConfig}.Install(s.Handler.GoRestfulContainer)
|
||||
}
|
||||
if s.openAPIConfig != nil {
|
||||
routes.OpenAPI{
|
||||
Config: s.openAPIConfig,
|
||||
}.Install(s.Handler.GoRestfulContainer, s.Handler.NonGoRestfulMux)
|
||||
}
|
||||
|
||||
s.installHealthz()
|
||||
|
||||
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
|
||||
|
||||
if s.GenericAPIServer.AuditBackend != nil {
|
||||
s.GenericAPIServer.AuditBackend.Shutdown()
|
||||
}
|
||||
|
||||
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 internal stop channel to allow cleanup of the listeners on error.
|
||||
internalStopCh := make(chan struct{})
|
||||
|
||||
if s.SecureServingInfo != nil && s.Handler != nil {
|
||||
if err := s.serveSecurely(internalStopCh); 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)
|
||||
}()
|
||||
|
||||
// Start the audit backend before any request comes in. This means we cannot turn it into a
|
||||
// post start hook because without calling Backend.Run the Backend.ProcessEvents call might block.
|
||||
if s.AuditBackend != nil {
|
||||
if err := s.AuditBackend.Run(stopCh); err != nil {
|
||||
return fmt.Errorf("failed to run the audit backend: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
s.RunPostStartHooks(stopCh)
|
||||
|
||||
if _, err := systemd.SdNotify(true, "READY=1\n"); err != nil {
|
||||
glog.Errorf("Unable to send systemd daemon successful start message: %v\n", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// EffectiveSecurePort returns the secure port we bound to.
|
||||
func (s *GenericAPIServer) EffectiveSecurePort() int {
|
||||
return s.effectiveSecurePort
|
||||
}
|
||||
|
||||
// installAPIResources is a private method for installing the REST storage backing each api groupversionresource
|
||||
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo) error {
|
||||
for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions {
|
||||
if len(apiGroupInfo.VersionedResourcesStorageMap[groupVersion.Version]) == 0 {
|
||||
glog.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
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
if err := s.installAPIResources(apiPrefix, apiGroupInfo); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// setup discovery
|
||||
apiVersions := []string{}
|
||||
for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions {
|
||||
apiVersions = append(apiVersions, groupVersion.Version)
|
||||
}
|
||||
// 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, apiVersions, s.requestContextMapper).WebService())
|
||||
return nil
|
||||
}
|
||||
|
||||
// Exposes the given api group in the API.
|
||||
func (s *GenericAPIServer) InstallAPIGroup(apiGroupInfo *APIGroupInfo) error {
|
||||
// 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.GroupMeta.GroupVersion.Group) == 0 {
|
||||
return fmt.Errorf("cannot register handler with an empty group for %#v", *apiGroupInfo)
|
||||
}
|
||||
if len(apiGroupInfo.GroupMeta.GroupVersion.Version) == 0 {
|
||||
return fmt.Errorf("cannot register handler with an empty version for %#v", *apiGroupInfo)
|
||||
}
|
||||
|
||||
if err := s.installAPIResources(APIGroupPrefix, apiGroupInfo); err != nil {
|
||||
return 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.GroupMeta.GroupVersions {
|
||||
// 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.GroupMeta.GroupVersion.String(),
|
||||
Version: apiGroupInfo.GroupMeta.GroupVersion.Version,
|
||||
}
|
||||
apiGroup := metav1.APIGroup{
|
||||
Name: apiGroupInfo.GroupMeta.GroupVersion.Group,
|
||||
Versions: apiVersionsForDiscovery,
|
||||
PreferredVersion: preferredVersionForDiscovery,
|
||||
}
|
||||
|
||||
s.DiscoveryGroupManager.AddGroup(apiGroup)
|
||||
s.Handler.GoRestfulContainer.Add(discovery.NewAPIGroupHandler(s.Serializer, apiGroup, s.requestContextMapper).WebService())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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),
|
||||
Copier: apiGroupInfo.Scheme,
|
||||
Defaulter: apiGroupInfo.Scheme,
|
||||
Typer: apiGroupInfo.Scheme,
|
||||
SubresourceGroupVersionKind: apiGroupInfo.SubresourceGroupVersionKind,
|
||||
Linker: apiGroupInfo.GroupMeta.SelfLinker,
|
||||
Mapper: apiGroupInfo.GroupMeta.RESTMapper,
|
||||
|
||||
Admit: s.admissionControl,
|
||||
Context: s.RequestContextMapper(),
|
||||
MinRequestTimeout: s.minRequestTimeout,
|
||||
EnableAPIResponseCompression: s.enableAPIResponseCompression,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultAPIGroupInfo returns an APIGroupInfo stubbed with "normal" values
|
||||
// exposed for easier composition from other packages
|
||||
func NewDefaultAPIGroupInfo(group string, registry *registered.APIRegistrationManager, scheme *runtime.Scheme, parameterCodec runtime.ParameterCodec, codecs serializer.CodecFactory) APIGroupInfo {
|
||||
groupMeta := registry.GroupOrDie(group)
|
||||
|
||||
return APIGroupInfo{
|
||||
GroupMeta: *groupMeta,
|
||||
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,
|
||||
}
|
||||
}
|
||||
511
vendor/k8s.io/apiserver/pkg/server/genericapiserver_test.go
generated
vendored
Normal file
511
vendor/k8s.io/apiserver/pkg/server/genericapiserver_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,511 @@
|
|||
/*
|
||||
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
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
goruntime "runtime"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
// "github.com/go-openapi/spec"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
"k8s.io/apimachinery/pkg/apimachinery"
|
||||
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"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/apiserver/pkg/apis/example"
|
||||
examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/user"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/endpoints/discovery"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
etcdtesting "k8s.io/apiserver/pkg/storage/etcd/testing"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
openapi "k8s.io/kube-openapi/pkg/common"
|
||||
)
|
||||
|
||||
const (
|
||||
extensionsGroupName = "extensions"
|
||||
)
|
||||
|
||||
var (
|
||||
v1GroupVersion = schema.GroupVersion{Group: "", Version: "v1"}
|
||||
|
||||
scheme = runtime.NewScheme()
|
||||
codecs = serializer.NewCodecFactory(scheme)
|
||||
parameterCodec = runtime.NewParameterCodec(scheme)
|
||||
)
|
||||
|
||||
func init() {
|
||||
metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
|
||||
scheme.AddUnversionedTypes(v1GroupVersion,
|
||||
&metav1.Status{},
|
||||
&metav1.APIVersions{},
|
||||
&metav1.APIGroupList{},
|
||||
&metav1.APIGroup{},
|
||||
&metav1.APIResourceList{},
|
||||
)
|
||||
example.AddToScheme(scheme)
|
||||
examplev1.AddToScheme(scheme)
|
||||
}
|
||||
|
||||
func testGetOpenAPIDefinitions(ref openapi.ReferenceCallback) map[string]openapi.OpenAPIDefinition {
|
||||
return map[string]openapi.OpenAPIDefinition{}
|
||||
}
|
||||
|
||||
// setUp is a convience function for setting up for (most) tests.
|
||||
func setUp(t *testing.T) (*etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
|
||||
etcdServer, _ := etcdtesting.NewUnsecuredEtcd3TestClientServer(t, scheme)
|
||||
|
||||
config := NewConfig(codecs)
|
||||
config.PublicAddress = net.ParseIP("192.168.10.4")
|
||||
config.RequestContextMapper = genericapirequest.NewRequestContextMapper()
|
||||
config.LegacyAPIGroupPrefixes = sets.NewString("/api")
|
||||
config.LoopbackClientConfig = &restclient.Config{}
|
||||
|
||||
clientset := fake.NewSimpleClientset()
|
||||
if clientset == nil {
|
||||
t.Fatal("unable to create fake client set")
|
||||
}
|
||||
|
||||
// TODO restore this test, but right now, eliminate our cycle
|
||||
// config.OpenAPIConfig = DefaultOpenAPIConfig(testGetOpenAPIDefinitions, runtime.NewScheme())
|
||||
// config.OpenAPIConfig.Info = &spec.Info{
|
||||
// InfoProps: spec.InfoProps{
|
||||
// Title: "Kubernetes",
|
||||
// Version: "unversioned",
|
||||
// },
|
||||
// }
|
||||
config.SwaggerConfig = DefaultSwaggerConfig()
|
||||
sharedInformers := informers.NewSharedInformerFactory(clientset, config.LoopbackClientConfig.Timeout)
|
||||
config.Complete(sharedInformers)
|
||||
|
||||
return etcdServer, *config, assert.New(t)
|
||||
}
|
||||
|
||||
func newMaster(t *testing.T) (*GenericAPIServer, *etcdtesting.EtcdTestServer, Config, *assert.Assertions) {
|
||||
etcdserver, config, assert := setUp(t)
|
||||
|
||||
s, err := config.Complete(nil).New("test", EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the server: %v", err)
|
||||
}
|
||||
return s, etcdserver, config, assert
|
||||
}
|
||||
|
||||
// TestNew verifies that the New function returns a GenericAPIServer
|
||||
// using the configuration properly.
|
||||
func TestNew(t *testing.T) {
|
||||
s, etcdserver, config, assert := newMaster(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
// Verify many of the variables match their config counterparts
|
||||
assert.Equal(s.legacyAPIGroupPrefixes, config.LegacyAPIGroupPrefixes)
|
||||
assert.Equal(s.admissionControl, config.AdmissionControl)
|
||||
assert.Equal(s.RequestContextMapper(), config.RequestContextMapper)
|
||||
|
||||
// these values get defaulted
|
||||
assert.Equal(net.JoinHostPort(config.PublicAddress.String(), "443"), s.ExternalAddress)
|
||||
assert.NotNil(s.swaggerConfig)
|
||||
assert.Equal("http://"+s.ExternalAddress, s.swaggerConfig.WebServicesUrl)
|
||||
}
|
||||
|
||||
// Verifies that AddGroupVersions works as expected.
|
||||
func TestInstallAPIGroups(t *testing.T) {
|
||||
etcdserver, config, assert := setUp(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
config.LegacyAPIGroupPrefixes = sets.NewString("/apiPrefix")
|
||||
config.DiscoveryAddresses = discovery.DefaultAddresses{DefaultAddress: "ExternalAddress"}
|
||||
|
||||
s, err := config.Complete(nil).New("test", EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the server: %v", err)
|
||||
}
|
||||
|
||||
testAPI := func(gv schema.GroupVersion) APIGroupInfo {
|
||||
getter, noVerbs := testGetterStorage{}, testNoVerbsStorage{}
|
||||
|
||||
scheme := runtime.NewScheme()
|
||||
scheme.AddKnownTypeWithName(gv.WithKind("Getter"), getter.New())
|
||||
scheme.AddKnownTypeWithName(gv.WithKind("NoVerb"), noVerbs.New())
|
||||
scheme.AddKnownTypes(v1GroupVersion, &metav1.Status{})
|
||||
metav1.AddToGroupVersion(scheme, v1GroupVersion)
|
||||
|
||||
interfacesFor := func(version schema.GroupVersion) (*meta.VersionInterfaces, error) {
|
||||
return &meta.VersionInterfaces{
|
||||
ObjectConvertor: scheme,
|
||||
MetadataAccessor: meta.NewAccessor(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
mapper := meta.NewDefaultRESTMapper([]schema.GroupVersion{gv}, interfacesFor)
|
||||
for kind := range scheme.KnownTypes(gv) {
|
||||
mapper.Add(gv.WithKind(kind), meta.RESTScopeNamespace)
|
||||
}
|
||||
groupMeta := apimachinery.GroupMeta{
|
||||
GroupVersion: gv,
|
||||
GroupVersions: []schema.GroupVersion{gv},
|
||||
RESTMapper: mapper,
|
||||
InterfacesFor: interfacesFor,
|
||||
}
|
||||
|
||||
return APIGroupInfo{
|
||||
GroupMeta: groupMeta,
|
||||
VersionedResourcesStorageMap: map[string]map[string]rest.Storage{
|
||||
gv.Version: {
|
||||
"getter": &testGetterStorage{Version: gv.Version},
|
||||
"noverbs": &testNoVerbsStorage{Version: gv.Version},
|
||||
},
|
||||
},
|
||||
OptionsExternalVersion: &schema.GroupVersion{Version: "v1"},
|
||||
ParameterCodec: parameterCodec,
|
||||
NegotiatedSerializer: codecs,
|
||||
Scheme: scheme,
|
||||
}
|
||||
}
|
||||
|
||||
apis := []APIGroupInfo{
|
||||
testAPI(schema.GroupVersion{Group: "", Version: "v1"}),
|
||||
testAPI(schema.GroupVersion{Group: extensionsGroupName, Version: "v1"}),
|
||||
testAPI(schema.GroupVersion{Group: "batch", Version: "v1"}),
|
||||
}
|
||||
|
||||
err = s.InstallLegacyAPIGroup("/apiPrefix", &apis[0])
|
||||
assert.NoError(err)
|
||||
groupPaths := []string{
|
||||
config.LegacyAPIGroupPrefixes.List()[0], // /apiPrefix
|
||||
}
|
||||
for _, api := range apis[1:] {
|
||||
err = s.InstallAPIGroup(&api)
|
||||
assert.NoError(err)
|
||||
groupPaths = append(groupPaths, APIGroupPrefix+"/"+api.GroupMeta.GroupVersion.Group) // /apis/<group>
|
||||
}
|
||||
|
||||
server := httptest.NewServer(s.Handler)
|
||||
defer server.Close()
|
||||
|
||||
for i := range apis {
|
||||
// should serve APIGroup at group path
|
||||
info := &apis[i]
|
||||
path := groupPaths[i]
|
||||
resp, err := http.Get(server.URL + path)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error getting path %q path: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error reading body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
t.Logf("[%d] json at %s: %s", i, path, string(body))
|
||||
|
||||
if i == 0 {
|
||||
// legacy API returns APIVersions
|
||||
group := metav1.APIVersions{}
|
||||
err = json.Unmarshal(body, &group)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// API groups return APIGroup
|
||||
group := metav1.APIGroup{}
|
||||
err = json.Unmarshal(body, &group)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if got, expected := group.Name, info.GroupMeta.GroupVersion.Group; got != expected {
|
||||
t.Errorf("[%d] unexpected group name at path %q: got=%q expected=%q", i, path, got, expected)
|
||||
continue
|
||||
}
|
||||
|
||||
if got, expected := group.PreferredVersion.Version, info.GroupMeta.GroupVersion.Version; got != expected {
|
||||
t.Errorf("[%d] unexpected group version at path %q: got=%q expected=%q", i, path, got, expected)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// should serve APIResourceList at group path + /<group-version>
|
||||
path = path + "/" + info.GroupMeta.GroupVersion.Version
|
||||
resp, err = http.Get(server.URL + path)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error getting path %q path: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
body, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error reading body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
t.Logf("[%d] json at %s: %s", i, path, string(body))
|
||||
|
||||
resources := metav1.APIResourceList{}
|
||||
err = json.Unmarshal(body, &resources)
|
||||
if err != nil {
|
||||
t.Errorf("[%d] unexpected error parsing json body at path %q: %v", i, path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if got, expected := resources.GroupVersion, info.GroupMeta.GroupVersion.String(); got != expected {
|
||||
t.Errorf("[%d] unexpected groupVersion at path %q: got=%q expected=%q", i, path, got, expected)
|
||||
continue
|
||||
}
|
||||
|
||||
// the verbs should match the features of resources
|
||||
for _, r := range resources.APIResources {
|
||||
switch r.Name {
|
||||
case "getter":
|
||||
if got, expected := sets.NewString([]string(r.Verbs)...), sets.NewString("get"); !got.Equal(expected) {
|
||||
t.Errorf("[%d] unexpected verbs for resource %s/%s: got=%v expected=%v", i, resources.GroupVersion, r.Name, got, expected)
|
||||
}
|
||||
case "noverbs":
|
||||
if r.Verbs == nil {
|
||||
t.Errorf("[%d] unexpected nil verbs slice. Expected: []string{}", i)
|
||||
}
|
||||
if got, expected := sets.NewString([]string(r.Verbs)...), sets.NewString(); !got.Equal(expected) {
|
||||
t.Errorf("[%d] unexpected verbs for resource %s/%s: got=%v expected=%v", i, resources.GroupVersion, r.Name, got, expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrepareRun(t *testing.T) {
|
||||
s, etcdserver, config, assert := newMaster(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
assert.NotNil(config.SwaggerConfig)
|
||||
|
||||
server := httptest.NewServer(s.Handler.Director)
|
||||
defer server.Close()
|
||||
done := make(chan struct{})
|
||||
|
||||
s.PrepareRun()
|
||||
s.RunPostStartHooks(done)
|
||||
|
||||
// swagger is installed in PrepareRun
|
||||
resp, err := http.Get(server.URL + "/swaggerapi/")
|
||||
assert.NoError(err)
|
||||
assert.Equal(http.StatusOK, resp.StatusCode)
|
||||
|
||||
// healthz checks are installed in PrepareRun
|
||||
resp, err = http.Get(server.URL + "/healthz")
|
||||
assert.NoError(err)
|
||||
assert.Equal(http.StatusOK, resp.StatusCode)
|
||||
resp, err = http.Get(server.URL + "/healthz/ping")
|
||||
assert.NoError(err)
|
||||
assert.Equal(http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
// TestCustomHandlerChain verifies the handler chain with custom handler chain builder functions.
|
||||
func TestCustomHandlerChain(t *testing.T) {
|
||||
etcdserver, config, _ := setUp(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
var protected, called bool
|
||||
|
||||
config.BuildHandlerChainFunc = func(apiHandler http.Handler, c *Config) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
protected = true
|
||||
apiHandler.ServeHTTP(w, req)
|
||||
})
|
||||
}
|
||||
handler := http.HandlerFunc(func(r http.ResponseWriter, req *http.Request) {
|
||||
called = true
|
||||
})
|
||||
|
||||
s, err := config.Complete(nil).New("test", EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the server: %v", err)
|
||||
}
|
||||
|
||||
s.Handler.NonGoRestfulMux.Handle("/nonswagger", handler)
|
||||
s.Handler.NonGoRestfulMux.Handle("/secret", handler)
|
||||
|
||||
type Test struct {
|
||||
handler http.Handler
|
||||
path string
|
||||
protected bool
|
||||
}
|
||||
for i, test := range []Test{
|
||||
{s.Handler, "/nonswagger", true},
|
||||
{s.Handler, "/secret", true},
|
||||
} {
|
||||
protected, called = false, false
|
||||
|
||||
var w io.Reader
|
||||
req, err := http.NewRequest("GET", test.path, w)
|
||||
if err != nil {
|
||||
t.Errorf("%d: Unexpected http error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
|
||||
test.handler.ServeHTTP(httptest.NewRecorder(), req)
|
||||
|
||||
if !called {
|
||||
t.Errorf("%d: Expected handler to be called.", i)
|
||||
}
|
||||
if test.protected != protected {
|
||||
t.Errorf("%d: Expected protected=%v, got protected=%v.", i, test.protected, protected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNotRestRoutesHaveAuth checks that special non-routes are behind authz/authn.
|
||||
func TestNotRestRoutesHaveAuth(t *testing.T) {
|
||||
etcdserver, config, _ := setUp(t)
|
||||
defer etcdserver.Terminate(t)
|
||||
|
||||
authz := mockAuthorizer{}
|
||||
|
||||
config.LegacyAPIGroupPrefixes = sets.NewString("/apiPrefix")
|
||||
config.Authorizer = &authz
|
||||
|
||||
config.EnableSwaggerUI = true
|
||||
config.EnableIndex = true
|
||||
config.EnableProfiling = true
|
||||
config.SwaggerConfig = DefaultSwaggerConfig()
|
||||
|
||||
kubeVersion := fakeVersion()
|
||||
config.Version = &kubeVersion
|
||||
|
||||
s, err := config.Complete(nil).New("test", EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Fatalf("Error in bringing up the server: %v", err)
|
||||
}
|
||||
|
||||
for _, test := range []struct {
|
||||
route string
|
||||
}{
|
||||
{"/"},
|
||||
{"/swagger-ui/"},
|
||||
{"/debug/pprof/"},
|
||||
{"/version"},
|
||||
} {
|
||||
resp := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("GET", test.route, nil)
|
||||
s.Handler.ServeHTTP(resp, req)
|
||||
if resp.Code != 200 {
|
||||
t.Errorf("route %q expected to work: code %d", test.route, resp.Code)
|
||||
continue
|
||||
}
|
||||
|
||||
if authz.lastURI != test.route {
|
||||
t.Errorf("route %q expected to go through authorization, last route did: %q", test.route, authz.lastURI)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type mockAuthorizer struct {
|
||||
lastURI string
|
||||
}
|
||||
|
||||
func (authz *mockAuthorizer) Authorize(a authorizer.Attributes) (authorized bool, reason string, err error) {
|
||||
authz.lastURI = a.GetPath()
|
||||
return true, "", nil
|
||||
}
|
||||
|
||||
type mockAuthenticator struct {
|
||||
lastURI string
|
||||
}
|
||||
|
||||
func (authn *mockAuthenticator) AuthenticateRequest(req *http.Request) (user.Info, bool, error) {
|
||||
authn.lastURI = req.RequestURI
|
||||
return &user.DefaultInfo{
|
||||
Name: "foo",
|
||||
}, true, nil
|
||||
}
|
||||
|
||||
func decodeResponse(resp *http.Response, obj interface{}) error {
|
||||
defer resp.Body.Close()
|
||||
|
||||
data, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(data, obj); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type testGetterStorage struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
func (p *testGetterStorage) New() runtime.Object {
|
||||
return &metav1.APIGroup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "Getter",
|
||||
APIVersion: p.Version,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *testGetterStorage) Get(ctx genericapirequest.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type testNoVerbsStorage struct {
|
||||
Version string
|
||||
}
|
||||
|
||||
func (p *testNoVerbsStorage) New() runtime.Object {
|
||||
return &metav1.APIGroup{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "NoVerbs",
|
||||
APIVersion: p.Version,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func fakeVersion() version.Info {
|
||||
return version.Info{
|
||||
Major: "42",
|
||||
Minor: "42",
|
||||
GitVersion: "42",
|
||||
GitCommit: "34973274ccef6ab4dfaaf86599792fa9c3fe4689",
|
||||
GitTreeState: "Dirty",
|
||||
BuildDate: time.Now().String(),
|
||||
GoVersion: goruntime.Version(),
|
||||
Compiler: goruntime.Compiler,
|
||||
Platform: fmt.Sprintf("%s/%s", goruntime.GOOS, goruntime.GOARCH),
|
||||
}
|
||||
}
|
||||
199
vendor/k8s.io/apiserver/pkg/server/handler.go
generated
vendored
Normal file
199
vendor/k8s.io/apiserver/pkg/server/handler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
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"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
rt "runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
|
||||
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/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
genericmux "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 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, contextMapper request.RequestContextMapper, s runtime.NegotiatedSerializer, handlerChainBuilder HandlerChainBuilderFn, notFoundHandler http.Handler) *APIServerHandler {
|
||||
nonGoRestfulMux := genericmux.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) {
|
||||
ctx, ok := contextMapper.Get(request.Request)
|
||||
if !ok {
|
||||
responsewriters.InternalError(response.ResponseWriter, request.Request, errors.New("no context found for request"))
|
||||
}
|
||||
serviceErrorHandler(ctx, 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/" {
|
||||
glog.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())] == '/' {
|
||||
glog.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
|
||||
glog.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))
|
||||
}
|
||||
glog.Errorln(buffer.String())
|
||||
|
||||
headers := http.Header{}
|
||||
if ct := w.Header().Get("Content-Type"); len(ct) > 0 {
|
||||
headers.Set("Accept", ct)
|
||||
}
|
||||
emptyContext := request.NewContext() // best we can do here: we don't know the request
|
||||
responsewriters.ErrorNegotiated(emptyContext, apierrors.NewGenericServerResponse(http.StatusInternalServerError, "", schema.GroupResource{}, "", "", 0, false), s, schema.GroupVersion{}, w, &http.Request{Header: headers})
|
||||
}
|
||||
|
||||
func serviceErrorHandler(ctx request.Context, s runtime.NegotiatedSerializer, serviceErr restful.ServiceError, request *restful.Request, resp *restful.Response) {
|
||||
responsewriters.ErrorNegotiated(
|
||||
ctx,
|
||||
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
Normal file
45
vendor/k8s.io/apiserver/pkg/server/healthz.go
generated
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
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...)
|
||||
}
|
||||
2
vendor/k8s.io/apiserver/pkg/server/healthz/doc.go
generated
vendored
2
vendor/k8s.io/apiserver/pkg/server/healthz/doc.go
generated
vendored
|
|
@ -18,4 +18,4 @@ limitations under the License.
|
|||
// Usage:
|
||||
// import "k8s.io/apiserver/pkg/server/healthz"
|
||||
// healthz.DefaultHealthz()
|
||||
package healthz
|
||||
package healthz // import "k8s.io/apiserver/pkg/server/healthz"
|
||||
|
|
|
|||
112
vendor/k8s.io/apiserver/pkg/server/healthz/healthz_test.go
generated
vendored
Normal file
112
vendor/k8s.io/apiserver/pkg/server/healthz/healthz_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
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 healthz
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInstallHandler(t *testing.T) {
|
||||
mux := http.NewServeMux()
|
||||
InstallHandler(mux)
|
||||
req, err := http.NewRequest("GET", "http://example.com/healthz", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
mux.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Errorf("expected %v, got %v", http.StatusOK, w.Code)
|
||||
}
|
||||
if w.Body.String() != "ok" {
|
||||
t.Errorf("expected %v, got %v", "ok", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestMulitipleChecks(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
expectedResponse string
|
||||
expectedStatus int
|
||||
addBadCheck bool
|
||||
}{
|
||||
{"/healthz?verbose", "[+]ping ok\nhealthz check passed\n", http.StatusOK, false},
|
||||
{"/healthz/ping", "ok", http.StatusOK, false},
|
||||
{"/healthz", "ok", http.StatusOK, false},
|
||||
{"/healthz?verbose", "[+]ping ok\n[-]bad failed: reason withheld\nhealthz check failed\n", http.StatusInternalServerError, true},
|
||||
{"/healthz/ping", "ok", http.StatusOK, true},
|
||||
{"/healthz/bad", "internal server error: this will fail\n", http.StatusInternalServerError, true},
|
||||
{"/healthz", "[+]ping ok\n[-]bad failed: reason withheld\nhealthz check failed\n", http.StatusInternalServerError, true},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
mux := http.NewServeMux()
|
||||
checks := []HealthzChecker{PingHealthz}
|
||||
if test.addBadCheck {
|
||||
checks = append(checks, NamedCheck("bad", func(_ *http.Request) error {
|
||||
return errors.New("this will fail")
|
||||
}))
|
||||
}
|
||||
InstallHandler(mux, checks...)
|
||||
req, err := http.NewRequest("GET", fmt.Sprintf("http://example.com%v", test.path), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("case[%d] Unexpected error: %v", i, err)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
mux.ServeHTTP(w, req)
|
||||
if w.Code != test.expectedStatus {
|
||||
t.Errorf("case[%d] Expected: %v, got: %v", i, test.expectedStatus, w.Code)
|
||||
}
|
||||
if w.Body.String() != test.expectedResponse {
|
||||
t.Errorf("case[%d] Expected:\n%v\ngot:\n%v\n", i, test.expectedResponse, w.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckerNames(t *testing.T) {
|
||||
n1 := "n1"
|
||||
n2 := "n2"
|
||||
c1 := &healthzCheck{name: n1}
|
||||
c2 := &healthzCheck{name: n2}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
have []HealthzChecker
|
||||
want []string
|
||||
}{
|
||||
{"no checker", []HealthzChecker{}, []string{}},
|
||||
{"one checker", []HealthzChecker{c1}, []string{n1}},
|
||||
{"other checker", []HealthzChecker{c2}, []string{n2}},
|
||||
{"checker order", []HealthzChecker{c1, c2}, []string{n1, n2}},
|
||||
{"different checker order", []HealthzChecker{c2, c1}, []string{n2, n1}},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
result := checkerNames(tc.have...)
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
if reflect.DeepEqual(tc.want, result) {
|
||||
t.Errorf("want %#v, got %#v", tc.want, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
162
vendor/k8s.io/apiserver/pkg/server/hooks.go
generated
vendored
Normal file
162
vendor/k8s.io/apiserver/pkg/server/hooks.go
generated
vendored
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
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"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
// done will be closed when the postHook is finished
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// 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 _, exists := s.postStartHooks[name]; exists {
|
||||
return fmt.Errorf("unable to add %q because it is already registered", name)
|
||||
}
|
||||
|
||||
// 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{})
|
||||
s.AddHealthzChecks(postStartHookHealthz{name: "poststarthook/" + name, done: done})
|
||||
s.postStartHooks[name] = postStartHookEntry{hook: hook, 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 {
|
||||
glog.Fatalf("Error registering PostStartHook %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)
|
||||
}
|
||||
}
|
||||
|
||||
// isHookRegistered checks whether a given hook is registered
|
||||
func (s *GenericAPIServer) isHookRegistered(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 {
|
||||
glog.Fatalf("PostStartHook %q failed: %v", name, err)
|
||||
}
|
||||
close(entry.done)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
35
vendor/k8s.io/apiserver/pkg/server/httplog/BUILD
generated
vendored
Normal file
35
vendor/k8s.io/apiserver/pkg/server/httplog/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["httplog_test.go"],
|
||||
library = ":go_default_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"httplog.go",
|
||||
],
|
||||
deps = ["//vendor/github.com/golang/glog:go_default_library"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
19
vendor/k8s.io/apiserver/pkg/server/httplog/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/apiserver/pkg/server/httplog/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
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 httplog contains a helper object and functions to maintain a log
|
||||
// along with an http response.
|
||||
package httplog // import "k8s.io/apiserver/pkg/server/httplog"
|
||||
213
vendor/k8s.io/apiserver/pkg/server/httplog/httplog.go
generated
vendored
Normal file
213
vendor/k8s.io/apiserver/pkg/server/httplog/httplog.go
generated
vendored
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
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 httplog
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// StacktracePred returns true if a stacktrace should be logged for this status.
|
||||
type StacktracePred func(httpStatus int) (logStacktrace bool)
|
||||
|
||||
type logger interface {
|
||||
Addf(format string, data ...interface{})
|
||||
}
|
||||
|
||||
// Add a layer on top of ResponseWriter, so we can track latency and error
|
||||
// message sources.
|
||||
//
|
||||
// TODO now that we're using go-restful, we shouldn't need to be wrapping
|
||||
// the http.ResponseWriter. We can recover panics from go-restful, and
|
||||
// the logging value is questionable.
|
||||
type respLogger struct {
|
||||
hijacked bool
|
||||
statusRecorded bool
|
||||
status int
|
||||
statusStack string
|
||||
addedInfo string
|
||||
startTime time.Time
|
||||
|
||||
captureErrorOutput bool
|
||||
|
||||
req *http.Request
|
||||
w http.ResponseWriter
|
||||
|
||||
logStacktracePred StacktracePred
|
||||
}
|
||||
|
||||
// Simple logger that logs immediately when Addf is called
|
||||
type passthroughLogger struct{}
|
||||
|
||||
// Addf logs info immediately.
|
||||
func (passthroughLogger) Addf(format string, data ...interface{}) {
|
||||
glog.V(2).Info(fmt.Sprintf(format, data...))
|
||||
}
|
||||
|
||||
// DefaultStacktracePred is the default implementation of StacktracePred.
|
||||
func DefaultStacktracePred(status int) bool {
|
||||
return (status < http.StatusOK || status >= http.StatusInternalServerError) && status != http.StatusSwitchingProtocols
|
||||
}
|
||||
|
||||
// NewLogged turns a normal response writer into a logged response writer.
|
||||
//
|
||||
// Usage:
|
||||
//
|
||||
// defer NewLogged(req, &w).StacktraceWhen(StatusIsNot(200, 202)).Log()
|
||||
//
|
||||
// (Only the call to Log() is deferred, so you can set everything up in one line!)
|
||||
//
|
||||
// Note that this *changes* your writer, to route response writing actions
|
||||
// through the logger.
|
||||
//
|
||||
// Use LogOf(w).Addf(...) to log something along with the response result.
|
||||
func NewLogged(req *http.Request, w *http.ResponseWriter) *respLogger {
|
||||
if _, ok := (*w).(*respLogger); ok {
|
||||
// Don't double-wrap!
|
||||
panic("multiple NewLogged calls!")
|
||||
}
|
||||
rl := &respLogger{
|
||||
startTime: time.Now(),
|
||||
req: req,
|
||||
w: *w,
|
||||
logStacktracePred: DefaultStacktracePred,
|
||||
}
|
||||
*w = rl // hijack caller's writer!
|
||||
return rl
|
||||
}
|
||||
|
||||
// LogOf returns the logger hiding in w. If there is not an existing logger
|
||||
// then a passthroughLogger will be created which will log to stdout immediately
|
||||
// when Addf is called.
|
||||
func LogOf(req *http.Request, w http.ResponseWriter) logger {
|
||||
if _, exists := w.(*respLogger); !exists {
|
||||
pl := &passthroughLogger{}
|
||||
return pl
|
||||
}
|
||||
if rl, ok := w.(*respLogger); ok {
|
||||
return rl
|
||||
}
|
||||
panic("Unable to find or create the logger!")
|
||||
}
|
||||
|
||||
// Unlogged returns the original ResponseWriter, or w if it is not our inserted logger.
|
||||
func Unlogged(w http.ResponseWriter) http.ResponseWriter {
|
||||
if rl, ok := w.(*respLogger); ok {
|
||||
return rl.w
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// StacktraceWhen sets the stacktrace logging predicate, which decides when to log a stacktrace.
|
||||
// There's a default, so you don't need to call this unless you don't like the default.
|
||||
func (rl *respLogger) StacktraceWhen(pred StacktracePred) *respLogger {
|
||||
rl.logStacktracePred = pred
|
||||
return rl
|
||||
}
|
||||
|
||||
// StatusIsNot returns a StacktracePred which will cause stacktraces to be logged
|
||||
// for any status *not* in the given list.
|
||||
func StatusIsNot(statuses ...int) StacktracePred {
|
||||
return func(status int) bool {
|
||||
for _, s := range statuses {
|
||||
if status == s {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// Addf adds additional data to be logged with this request.
|
||||
func (rl *respLogger) Addf(format string, data ...interface{}) {
|
||||
rl.addedInfo += "\n" + fmt.Sprintf(format, data...)
|
||||
}
|
||||
|
||||
// Log is intended to be called once at the end of your request handler, via defer
|
||||
func (rl *respLogger) Log() {
|
||||
latency := time.Since(rl.startTime)
|
||||
if glog.V(2) {
|
||||
if !rl.hijacked {
|
||||
glog.InfoDepth(1, fmt.Sprintf("%s %s: (%v) %v%v%v [%s %s]", rl.req.Method, rl.req.RequestURI, latency, rl.status, rl.statusStack, rl.addedInfo, rl.req.Header["User-Agent"], rl.req.RemoteAddr))
|
||||
} else {
|
||||
glog.InfoDepth(1, fmt.Sprintf("%s %s: (%v) hijacked [%s %s]", rl.req.Method, rl.req.RequestURI, latency, rl.req.Header["User-Agent"], rl.req.RemoteAddr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Header implements http.ResponseWriter.
|
||||
func (rl *respLogger) Header() http.Header {
|
||||
return rl.w.Header()
|
||||
}
|
||||
|
||||
// Write implements http.ResponseWriter.
|
||||
func (rl *respLogger) Write(b []byte) (int, error) {
|
||||
if !rl.statusRecorded {
|
||||
rl.recordStatus(http.StatusOK) // Default if WriteHeader hasn't been called
|
||||
}
|
||||
if rl.captureErrorOutput {
|
||||
rl.Addf("logging error output: %q\n", string(b))
|
||||
}
|
||||
return rl.w.Write(b)
|
||||
}
|
||||
|
||||
// Flush implements http.Flusher even if the underlying http.Writer doesn't implement it.
|
||||
// Flush is used for streaming purposes and allows to flush buffered data to the client.
|
||||
func (rl *respLogger) Flush() {
|
||||
if flusher, ok := rl.w.(http.Flusher); ok {
|
||||
flusher.Flush()
|
||||
} else if glog.V(2) {
|
||||
glog.InfoDepth(1, fmt.Sprintf("Unable to convert %+v into http.Flusher", rl.w))
|
||||
}
|
||||
}
|
||||
|
||||
// WriteHeader implements http.ResponseWriter.
|
||||
func (rl *respLogger) WriteHeader(status int) {
|
||||
rl.recordStatus(status)
|
||||
rl.w.WriteHeader(status)
|
||||
}
|
||||
|
||||
// Hijack implements http.Hijacker.
|
||||
func (rl *respLogger) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||
rl.hijacked = true
|
||||
return rl.w.(http.Hijacker).Hijack()
|
||||
}
|
||||
|
||||
// CloseNotify implements http.CloseNotifier
|
||||
func (rl *respLogger) CloseNotify() <-chan bool {
|
||||
return rl.w.(http.CloseNotifier).CloseNotify()
|
||||
}
|
||||
|
||||
func (rl *respLogger) recordStatus(status int) {
|
||||
rl.status = status
|
||||
rl.statusRecorded = true
|
||||
if rl.logStacktracePred(status) {
|
||||
// Only log stacks for errors
|
||||
stack := make([]byte, 50*1024)
|
||||
stack = stack[:runtime.Stack(stack, false)]
|
||||
rl.statusStack = "\n" + string(stack)
|
||||
rl.captureErrorOutput = true
|
||||
} else {
|
||||
rl.statusStack = ""
|
||||
}
|
||||
}
|
||||
152
vendor/k8s.io/apiserver/pkg/server/httplog/httplog_test.go
generated
vendored
Normal file
152
vendor/k8s.io/apiserver/pkg/server/httplog/httplog_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
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 httplog
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDefaultStacktracePred(t *testing.T) {
|
||||
for _, x := range []int{101, 200, 204, 302, 400, 404} {
|
||||
if DefaultStacktracePred(x) {
|
||||
t.Fatalf("should not log on %v by default", x)
|
||||
}
|
||||
}
|
||||
|
||||
for _, x := range []int{500, 100} {
|
||||
if !DefaultStacktracePred(x) {
|
||||
t.Fatalf("should log on %v by default", x)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusIsNot(t *testing.T) {
|
||||
statusTestTable := []struct {
|
||||
status int
|
||||
statuses []int
|
||||
want bool
|
||||
}{
|
||||
{http.StatusOK, []int{}, true},
|
||||
{http.StatusOK, []int{http.StatusOK}, false},
|
||||
{http.StatusCreated, []int{http.StatusOK, http.StatusAccepted}, true},
|
||||
}
|
||||
for _, tt := range statusTestTable {
|
||||
sp := StatusIsNot(tt.statuses...)
|
||||
got := sp(tt.status)
|
||||
if got != tt.want {
|
||||
t.Errorf("Expected %v, got %v", tt.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewLogged(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
NewLogged(req, &w)
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("Expected NewLogged to panic")
|
||||
}
|
||||
}()
|
||||
NewLogged(req, &w)
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
}
|
||||
|
||||
func TestLogOf(t *testing.T) {
|
||||
logOfTests := []bool{true, false}
|
||||
for _, makeLogger := range logOfTests {
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
var want string
|
||||
if makeLogger {
|
||||
NewLogged(req, &w)
|
||||
want = "*httplog.respLogger"
|
||||
} else {
|
||||
want = "*httplog.passthroughLogger"
|
||||
}
|
||||
got := reflect.TypeOf(LogOf(r, w)).String()
|
||||
if want != got {
|
||||
t.Errorf("Expected %v, got %v", want, got)
|
||||
}
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnlogged(t *testing.T) {
|
||||
unloggedTests := []bool{true, false}
|
||||
for _, makeLogger := range unloggedTests {
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error: %v", err)
|
||||
}
|
||||
handler := func(w http.ResponseWriter, r *http.Request) {
|
||||
want := w
|
||||
if makeLogger {
|
||||
NewLogged(req, &w)
|
||||
}
|
||||
got := Unlogged(w)
|
||||
if want != got {
|
||||
t.Errorf("Expected %v, got %v", want, got)
|
||||
}
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
handler(w, req)
|
||||
}
|
||||
}
|
||||
|
||||
type testResponseWriter struct{}
|
||||
|
||||
func (*testResponseWriter) Header() http.Header { return nil }
|
||||
func (*testResponseWriter) Write([]byte) (int, error) { return 0, nil }
|
||||
func (*testResponseWriter) WriteHeader(int) {}
|
||||
|
||||
func TestLoggedStatus(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
var tw http.ResponseWriter = new(testResponseWriter)
|
||||
logger := NewLogged(req, &tw)
|
||||
logger.Write(nil)
|
||||
|
||||
if logger.status != http.StatusOK {
|
||||
t.Errorf("expected status after write to be %v, got %v", http.StatusOK, logger.status)
|
||||
}
|
||||
|
||||
tw = new(testResponseWriter)
|
||||
logger = NewLogged(req, &tw)
|
||||
logger.WriteHeader(http.StatusForbidden)
|
||||
logger.Write(nil)
|
||||
|
||||
if logger.status != http.StatusForbidden {
|
||||
t.Errorf("expected status after write to remain %v, got %v", http.StatusForbidden, logger.status)
|
||||
}
|
||||
}
|
||||
40
vendor/k8s.io/apiserver/pkg/server/mux/BUILD
generated
vendored
Normal file
40
vendor/k8s.io/apiserver/pkg/server/mux/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["pathrecorder_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"pathrecorder.go",
|
||||
],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
2
vendor/k8s.io/apiserver/pkg/server/mux/OWNERS
generated
vendored
Executable file
2
vendor/k8s.io/apiserver/pkg/server/mux/OWNERS
generated
vendored
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
reviewers:
|
||||
- sttts
|
||||
18
vendor/k8s.io/apiserver/pkg/server/mux/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/apiserver/pkg/server/mux/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 mux contains abstractions for http multiplexing of APIs.
|
||||
package mux
|
||||
278
vendor/k8s.io/apiserver/pkg/server/mux/pathrecorder.go
generated
vendored
Normal file
278
vendor/k8s.io/apiserver/pkg/server/mux/pathrecorder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,278 @@
|
|||
/*
|
||||
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 mux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// PathRecorderMux wraps a mux object and records the registered exposedPaths.
|
||||
type PathRecorderMux struct {
|
||||
// name is used for logging so you can trace requests through
|
||||
name string
|
||||
|
||||
lock sync.Mutex
|
||||
notFoundHandler http.Handler
|
||||
pathToHandler map[string]http.Handler
|
||||
prefixToHandler map[string]http.Handler
|
||||
|
||||
// mux stores a pathHandler and is used to handle the actual serving.
|
||||
// Turns out, we want to accept trailing slashes, BUT we don't care about handling
|
||||
// everything under them. This does exactly matches only unless its explicitly requested to
|
||||
// do something different
|
||||
mux atomic.Value
|
||||
|
||||
// exposedPaths is the list of paths that should be shown at /
|
||||
exposedPaths []string
|
||||
|
||||
// pathStacks holds the stacks of all registered paths. This allows us to show a more helpful message
|
||||
// before the "http: multiple registrations for %s" panic.
|
||||
pathStacks map[string]string
|
||||
}
|
||||
|
||||
// pathHandler is an http.Handler that will satify requests first by exact match, then by prefix,
|
||||
// then by notFoundHandler
|
||||
type pathHandler struct {
|
||||
// muxName is used for logging so you can trace requests through
|
||||
muxName string
|
||||
|
||||
// pathToHandler is a map of exactly matching request to its handler
|
||||
pathToHandler map[string]http.Handler
|
||||
|
||||
// this has to be sorted by most slashes then by length
|
||||
prefixHandlers []prefixHandler
|
||||
|
||||
// notFoundHandler is the handler to use for satisfying requests with no other match
|
||||
notFoundHandler http.Handler
|
||||
}
|
||||
|
||||
// prefixHandler holds the prefix it should match and the handler to use
|
||||
type prefixHandler struct {
|
||||
// prefix is the prefix to test for a request match
|
||||
prefix string
|
||||
// handler is used to satisfy matching requests
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
// NewPathRecorderMux creates a new PathRecorderMux
|
||||
func NewPathRecorderMux(name string) *PathRecorderMux {
|
||||
ret := &PathRecorderMux{
|
||||
name: name,
|
||||
pathToHandler: map[string]http.Handler{},
|
||||
prefixToHandler: map[string]http.Handler{},
|
||||
mux: atomic.Value{},
|
||||
exposedPaths: []string{},
|
||||
pathStacks: map[string]string{},
|
||||
}
|
||||
|
||||
ret.mux.Store(&pathHandler{notFoundHandler: http.NotFoundHandler()})
|
||||
return ret
|
||||
}
|
||||
|
||||
// ListedPaths returns the registered handler exposedPaths.
|
||||
func (m *PathRecorderMux) ListedPaths() []string {
|
||||
handledPaths := append([]string{}, m.exposedPaths...)
|
||||
sort.Strings(handledPaths)
|
||||
|
||||
return handledPaths
|
||||
}
|
||||
|
||||
func (m *PathRecorderMux) trackCallers(path string) {
|
||||
if existingStack, ok := m.pathStacks[path]; ok {
|
||||
utilruntime.HandleError(fmt.Errorf("registered %q from %v", path, existingStack))
|
||||
}
|
||||
m.pathStacks[path] = string(debug.Stack())
|
||||
}
|
||||
|
||||
// refreshMuxLocked creates a new mux and must be called while locked. Otherwise the view of handlers may
|
||||
// not be consistent
|
||||
func (m *PathRecorderMux) refreshMuxLocked() {
|
||||
newMux := &pathHandler{
|
||||
muxName: m.name,
|
||||
pathToHandler: map[string]http.Handler{},
|
||||
prefixHandlers: []prefixHandler{},
|
||||
notFoundHandler: http.NotFoundHandler(),
|
||||
}
|
||||
if m.notFoundHandler != nil {
|
||||
newMux.notFoundHandler = m.notFoundHandler
|
||||
}
|
||||
for path, handler := range m.pathToHandler {
|
||||
newMux.pathToHandler[path] = handler
|
||||
}
|
||||
|
||||
keys := sets.StringKeySet(m.prefixToHandler).List()
|
||||
sort.Sort(sort.Reverse(byPrefixPriority(keys)))
|
||||
for _, prefix := range keys {
|
||||
newMux.prefixHandlers = append(newMux.prefixHandlers, prefixHandler{
|
||||
prefix: prefix,
|
||||
handler: m.prefixToHandler[prefix],
|
||||
})
|
||||
}
|
||||
|
||||
m.mux.Store(newMux)
|
||||
}
|
||||
|
||||
// NotFoundHandler sets the handler to use if there's no match for a give path
|
||||
func (m *PathRecorderMux) NotFoundHandler(notFoundHandler http.Handler) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
m.notFoundHandler = notFoundHandler
|
||||
|
||||
m.refreshMuxLocked()
|
||||
}
|
||||
|
||||
// Unregister removes a path from the mux.
|
||||
func (m *PathRecorderMux) Unregister(path string) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
delete(m.pathToHandler, path)
|
||||
delete(m.prefixToHandler, path)
|
||||
delete(m.pathStacks, path)
|
||||
for i := range m.exposedPaths {
|
||||
if m.exposedPaths[i] == path {
|
||||
m.exposedPaths = append(m.exposedPaths[:i], m.exposedPaths[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
m.refreshMuxLocked()
|
||||
}
|
||||
|
||||
// Handle registers the handler for the given pattern.
|
||||
// If a handler already exists for pattern, Handle panics.
|
||||
func (m *PathRecorderMux) Handle(path string, handler http.Handler) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
m.trackCallers(path)
|
||||
|
||||
m.exposedPaths = append(m.exposedPaths, path)
|
||||
m.pathToHandler[path] = handler
|
||||
m.refreshMuxLocked()
|
||||
}
|
||||
|
||||
// HandleFunc registers the handler function for the given pattern.
|
||||
// If a handler already exists for pattern, Handle panics.
|
||||
func (m *PathRecorderMux) HandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
|
||||
m.Handle(path, http.HandlerFunc(handler))
|
||||
}
|
||||
|
||||
// UnlistedHandle registers the handler for the given pattern, but doesn't list it.
|
||||
// If a handler already exists for pattern, Handle panics.
|
||||
func (m *PathRecorderMux) UnlistedHandle(path string, handler http.Handler) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
m.trackCallers(path)
|
||||
|
||||
m.pathToHandler[path] = handler
|
||||
m.refreshMuxLocked()
|
||||
}
|
||||
|
||||
// UnlistedHandleFunc registers the handler function for the given pattern, but doesn't list it.
|
||||
// If a handler already exists for pattern, Handle panics.
|
||||
func (m *PathRecorderMux) UnlistedHandleFunc(path string, handler func(http.ResponseWriter, *http.Request)) {
|
||||
m.UnlistedHandle(path, http.HandlerFunc(handler))
|
||||
}
|
||||
|
||||
// HandlePrefix is like Handle, but matches for anything under the path. Like a standard golang trailing slash.
|
||||
func (m *PathRecorderMux) HandlePrefix(path string, handler http.Handler) {
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
panic(fmt.Sprintf("%q must end in a trailing slash", path))
|
||||
}
|
||||
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
m.trackCallers(path)
|
||||
|
||||
m.exposedPaths = append(m.exposedPaths, path)
|
||||
m.prefixToHandler[path] = handler
|
||||
m.refreshMuxLocked()
|
||||
}
|
||||
|
||||
// UnlistedHandlePrefix is like UnlistedHandle, but matches for anything under the path. Like a standard golang trailing slash.
|
||||
func (m *PathRecorderMux) UnlistedHandlePrefix(path string, handler http.Handler) {
|
||||
if !strings.HasSuffix(path, "/") {
|
||||
panic(fmt.Sprintf("%q must end in a trailing slash", path))
|
||||
}
|
||||
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
m.trackCallers(path)
|
||||
|
||||
m.prefixToHandler[path] = handler
|
||||
m.refreshMuxLocked()
|
||||
}
|
||||
|
||||
// ServeHTTP makes it an http.Handler
|
||||
func (m *PathRecorderMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
m.mux.Load().(*pathHandler).ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// ServeHTTP makes it an http.Handler
|
||||
func (h *pathHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if exactHandler, ok := h.pathToHandler[r.URL.Path]; ok {
|
||||
glog.V(5).Infof("%v: %q satisfied by exact match", h.muxName, r.URL.Path)
|
||||
exactHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
for _, prefixHandler := range h.prefixHandlers {
|
||||
if strings.HasPrefix(r.URL.Path, prefixHandler.prefix) {
|
||||
glog.V(5).Infof("%v: %q satisfied by prefix %v", h.muxName, r.URL.Path, prefixHandler.prefix)
|
||||
prefixHandler.handler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(5).Infof("%v: %q satisfied by NotFoundHandler", h.muxName, r.URL.Path)
|
||||
h.notFoundHandler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// byPrefixPriority sorts url prefixes by the order in which they should be tested by the mux
|
||||
// this has to be sorted by most slashes then by length so that we can iterate straight
|
||||
// through to match the "best" one first.
|
||||
type byPrefixPriority []string
|
||||
|
||||
func (s byPrefixPriority) Len() int { return len(s) }
|
||||
func (s byPrefixPriority) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
func (s byPrefixPriority) Less(i, j int) bool {
|
||||
lhsNumParts := strings.Count(s[i], "/")
|
||||
rhsNumParts := strings.Count(s[j], "/")
|
||||
if lhsNumParts != rhsNumParts {
|
||||
return lhsNumParts < rhsNumParts
|
||||
}
|
||||
|
||||
lhsLen := len(s[i])
|
||||
rhsLen := len(s[j])
|
||||
if lhsLen != rhsLen {
|
||||
return lhsLen < rhsLen
|
||||
}
|
||||
|
||||
return strings.Compare(s[i], s[j]) < 0
|
||||
}
|
||||
135
vendor/k8s.io/apiserver/pkg/server/mux/pathrecorder_test.go
generated
vendored
Normal file
135
vendor/k8s.io/apiserver/pkg/server/mux/pathrecorder_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
/*
|
||||
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 mux
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestSecretHandlers(t *testing.T) {
|
||||
c := NewPathRecorderMux("test")
|
||||
c.UnlistedHandleFunc("/secret", func(http.ResponseWriter, *http.Request) {})
|
||||
c.HandleFunc("/nonswagger", func(http.ResponseWriter, *http.Request) {})
|
||||
assert.NotContains(t, c.ListedPaths(), "/secret")
|
||||
assert.Contains(t, c.ListedPaths(), "/nonswagger")
|
||||
}
|
||||
|
||||
func TestUnregisterHandlers(t *testing.T) {
|
||||
first := 0
|
||||
second := 0
|
||||
|
||||
c := NewPathRecorderMux("test")
|
||||
s := httptest.NewServer(c)
|
||||
defer s.Close()
|
||||
|
||||
c.UnlistedHandleFunc("/secret", func(http.ResponseWriter, *http.Request) {})
|
||||
c.HandleFunc("/nonswagger", func(http.ResponseWriter, *http.Request) {
|
||||
first = first + 1
|
||||
})
|
||||
assert.NotContains(t, c.ListedPaths(), "/secret")
|
||||
assert.Contains(t, c.ListedPaths(), "/nonswagger")
|
||||
|
||||
resp, _ := http.Get(s.URL + "/nonswagger")
|
||||
assert.Equal(t, first, 1)
|
||||
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||
|
||||
c.Unregister("/nonswagger")
|
||||
assert.NotContains(t, c.ListedPaths(), "/nonswagger")
|
||||
|
||||
resp, _ = http.Get(s.URL + "/nonswagger")
|
||||
assert.Equal(t, first, 1)
|
||||
assert.Equal(t, resp.StatusCode, http.StatusNotFound)
|
||||
|
||||
c.HandleFunc("/nonswagger", func(http.ResponseWriter, *http.Request) {
|
||||
second = second + 1
|
||||
})
|
||||
assert.Contains(t, c.ListedPaths(), "/nonswagger")
|
||||
resp, _ = http.Get(s.URL + "/nonswagger")
|
||||
assert.Equal(t, first, 1)
|
||||
assert.Equal(t, second, 1)
|
||||
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||
}
|
||||
|
||||
func TestPrefixHandlers(t *testing.T) {
|
||||
c := NewPathRecorderMux("test")
|
||||
s := httptest.NewServer(c)
|
||||
defer s.Close()
|
||||
|
||||
secretPrefixCount := 0
|
||||
c.UnlistedHandlePrefix("/secretPrefix/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
secretPrefixCount = secretPrefixCount + 1
|
||||
}))
|
||||
publicPrefixCount := 0
|
||||
c.HandlePrefix("/publicPrefix/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
publicPrefixCount = publicPrefixCount + 1
|
||||
}))
|
||||
precisePrefixCount := 0
|
||||
c.HandlePrefix("/publicPrefix/but-more-precise/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
precisePrefixCount = precisePrefixCount + 1
|
||||
}))
|
||||
exactMatchCount := 0
|
||||
c.Handle("/publicPrefix/exactmatch", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
exactMatchCount = exactMatchCount + 1
|
||||
}))
|
||||
slashMatchCount := 0
|
||||
c.Handle("/otherPublic/exactmatchslash/", http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
slashMatchCount = slashMatchCount + 1
|
||||
}))
|
||||
fallThroughCount := 0
|
||||
c.NotFoundHandler(http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
|
||||
fallThroughCount = fallThroughCount + 1
|
||||
}))
|
||||
|
||||
assert.NotContains(t, c.ListedPaths(), "/secretPrefix/")
|
||||
assert.Contains(t, c.ListedPaths(), "/publicPrefix/")
|
||||
|
||||
resp, _ := http.Get(s.URL + "/fallthrough")
|
||||
assert.Equal(t, 1, fallThroughCount)
|
||||
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||
resp, _ = http.Get(s.URL + "/publicPrefix")
|
||||
assert.Equal(t, 2, fallThroughCount)
|
||||
assert.Equal(t, resp.StatusCode, http.StatusOK)
|
||||
|
||||
http.Get(s.URL + "/publicPrefix/")
|
||||
assert.Equal(t, 1, publicPrefixCount)
|
||||
http.Get(s.URL + "/publicPrefix/something")
|
||||
assert.Equal(t, 2, publicPrefixCount)
|
||||
http.Get(s.URL + "/publicPrefix/but-more-precise")
|
||||
assert.Equal(t, 3, publicPrefixCount)
|
||||
http.Get(s.URL + "/publicPrefix/but-more-precise/")
|
||||
assert.Equal(t, 1, precisePrefixCount)
|
||||
http.Get(s.URL + "/publicPrefix/but-more-precise/more-stuff")
|
||||
assert.Equal(t, 2, precisePrefixCount)
|
||||
|
||||
http.Get(s.URL + "/publicPrefix/exactmatch")
|
||||
assert.Equal(t, 1, exactMatchCount)
|
||||
http.Get(s.URL + "/publicPrefix/exactmatch/")
|
||||
assert.Equal(t, 4, publicPrefixCount)
|
||||
http.Get(s.URL + "/otherPublic/exactmatchslash")
|
||||
assert.Equal(t, 3, fallThroughCount)
|
||||
http.Get(s.URL + "/otherPublic/exactmatchslash/")
|
||||
assert.Equal(t, 1, slashMatchCount)
|
||||
|
||||
http.Get(s.URL + "/secretPrefix/")
|
||||
assert.Equal(t, 1, secretPrefixCount)
|
||||
http.Get(s.URL + "/secretPrefix/something")
|
||||
assert.Equal(t, 2, secretPrefixCount)
|
||||
}
|
||||
102
vendor/k8s.io/apiserver/pkg/server/options/BUILD
generated
vendored
Normal file
102
vendor/k8s.io/apiserver/pkg/server/options/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["serving_test.go"],
|
||||
data = glob(["testdata/**"]),
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/request:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||
"//vendor/k8s.io/client-go/discovery:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"admission.go",
|
||||
"audit.go",
|
||||
"authentication.go",
|
||||
"authorization.go",
|
||||
"coreapi.go",
|
||||
"doc.go",
|
||||
"etcd.go",
|
||||
"feature.go",
|
||||
"recommended.go",
|
||||
"server_run_options.go",
|
||||
"serving.go",
|
||||
],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/gopkg.in/natefinch/lumberjack.v2:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/audit/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/audit:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/audit/policy:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authentication/authenticatorfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/healthz:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/etcd3/preflight:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/plugin/pkg/audit/log:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/plugin/pkg/audit/webhook:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/authentication/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/authorization/v1beta1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/kubernetes/typed/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/clientcmd:go_default_library",
|
||||
"//vendor/k8s.io/client-go/util/cert:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "testdata",
|
||||
srcs = glob(["testdata/*"]),
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
15
vendor/k8s.io/apiserver/pkg/server/options/OWNERS
generated
vendored
Executable file
15
vendor/k8s.io/apiserver/pkg/server/options/OWNERS
generated
vendored
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
reviewers:
|
||||
- smarterclayton
|
||||
- wojtek-t
|
||||
- deads2k
|
||||
- liggitt
|
||||
- nikhiljindal
|
||||
- sttts
|
||||
- jlowdermilk
|
||||
- soltysh
|
||||
- dims
|
||||
- cjcullen
|
||||
- ericchiang
|
||||
- ping035627
|
||||
- xiangpengzhao
|
||||
- enj
|
||||
97
vendor/k8s.io/apiserver/pkg/server/options/admission.go
generated
vendored
Normal file
97
vendor/k8s.io/apiserver/pkg/server/options/admission.go
generated
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/initializer"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
)
|
||||
|
||||
// AdmissionOptions holds the admission options
|
||||
type AdmissionOptions struct {
|
||||
PluginNames []string
|
||||
ConfigFile string
|
||||
Plugins *admission.Plugins
|
||||
}
|
||||
|
||||
// NewAdmissionOptions creates a new instance of AdmissionOptions
|
||||
// Note:
|
||||
// In addition it calls RegisterAllAdmissionPlugins to register
|
||||
// all generic admission plugins.
|
||||
func NewAdmissionOptions() *AdmissionOptions {
|
||||
options := &AdmissionOptions{
|
||||
Plugins: &admission.Plugins{},
|
||||
PluginNames: []string{},
|
||||
}
|
||||
server.RegisterAllAdmissionPlugins(options.Plugins)
|
||||
return options
|
||||
}
|
||||
|
||||
// AddFlags adds flags related to admission for a specific APIServer to the specified FlagSet
|
||||
func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringSliceVar(&a.PluginNames, "admission-control", a.PluginNames, ""+
|
||||
"Ordered list of plug-ins to do admission control of resources into cluster. "+
|
||||
"Comma-delimited list of: "+strings.Join(a.Plugins.Registered(), ", ")+".")
|
||||
|
||||
fs.StringVar(&a.ConfigFile, "admission-control-config-file", a.ConfigFile,
|
||||
"File with admission control configuration.")
|
||||
}
|
||||
|
||||
// ApplyTo adds the admission chain to the server configuration
|
||||
// the method lazily initializes a generic plugin that is appended to the list of pluginInitializers
|
||||
// note this method uses:
|
||||
// genericconfig.LoopbackClientConfig
|
||||
// genericconfig.SharedInformerFactory
|
||||
// genericconfig.Authorizer
|
||||
func (a *AdmissionOptions) ApplyTo(c *server.Config, informers informers.SharedInformerFactory, pluginInitializers ...admission.PluginInitializer) error {
|
||||
pluginsConfigProvider, err := admission.ReadAdmissionConfiguration(a.PluginNames, a.ConfigFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read plugin config: %v", err)
|
||||
}
|
||||
|
||||
clientset, err := kubernetes.NewForConfig(c.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
genericInitializer, err := initializer.New(clientset, informers, c.Authorizer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
initializersChain := admission.PluginInitializers{}
|
||||
pluginInitializers = append(pluginInitializers, genericInitializer)
|
||||
initializersChain = append(initializersChain, pluginInitializers...)
|
||||
|
||||
admissionChain, err := a.Plugins.NewFromPlugins(a.PluginNames, pluginsConfigProvider, initializersChain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.AdmissionControl = admissionChain
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *AdmissionOptions) Validate() []error {
|
||||
errs := []error{}
|
||||
return errs
|
||||
}
|
||||
265
vendor/k8s.io/apiserver/pkg/server/options/audit.go
generated
vendored
Normal file
265
vendor/k8s.io/apiserver/pkg/server/options/audit.go
generated
vendored
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
|
||||
auditv1beta1 "k8s.io/apiserver/pkg/apis/audit/v1beta1"
|
||||
"k8s.io/apiserver/pkg/audit"
|
||||
"k8s.io/apiserver/pkg/audit/policy"
|
||||
"k8s.io/apiserver/pkg/features"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
pluginlog "k8s.io/apiserver/plugin/pkg/audit/log"
|
||||
pluginwebhook "k8s.io/apiserver/plugin/pkg/audit/webhook"
|
||||
)
|
||||
|
||||
func appendBackend(existing, newBackend audit.Backend) audit.Backend {
|
||||
if existing == nil {
|
||||
return newBackend
|
||||
}
|
||||
return audit.Union(existing, newBackend)
|
||||
}
|
||||
|
||||
func advancedAuditingEnabled() bool {
|
||||
return utilfeature.DefaultFeatureGate.Enabled(features.AdvancedAuditing)
|
||||
}
|
||||
|
||||
type AuditOptions struct {
|
||||
// Policy configuration file for filtering audit events that are captured.
|
||||
// If unspecified, a default is provided.
|
||||
PolicyFile string
|
||||
|
||||
// Plugin options
|
||||
|
||||
LogOptions AuditLogOptions
|
||||
WebhookOptions AuditWebhookOptions
|
||||
}
|
||||
|
||||
// AuditLogOptions determines the output of the structured audit log by default.
|
||||
// If the AdvancedAuditing feature is set to false, AuditLogOptions holds the legacy
|
||||
// audit log writer.
|
||||
type AuditLogOptions struct {
|
||||
Path string
|
||||
MaxAge int
|
||||
MaxBackups int
|
||||
MaxSize int
|
||||
Format string
|
||||
}
|
||||
|
||||
// AuditWebhookOptions control the webhook configuration for audit events.
|
||||
type AuditWebhookOptions struct {
|
||||
ConfigFile string
|
||||
// Should the webhook asynchronous batch events to the webhook backend or
|
||||
// should the webhook block responses?
|
||||
//
|
||||
// Defaults to asynchronous batch events.
|
||||
Mode string
|
||||
}
|
||||
|
||||
func NewAuditOptions() *AuditOptions {
|
||||
return &AuditOptions{
|
||||
WebhookOptions: AuditWebhookOptions{Mode: pluginwebhook.ModeBatch},
|
||||
LogOptions: AuditLogOptions{Format: pluginlog.FormatJson},
|
||||
}
|
||||
}
|
||||
|
||||
// Validate checks invalid config combination
|
||||
func (o *AuditOptions) Validate() []error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrors := []error{}
|
||||
|
||||
if !advancedAuditingEnabled() {
|
||||
if len(o.PolicyFile) > 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("feature '%s' must be enabled to set option --audit-policy-file", features.AdvancedAuditing))
|
||||
}
|
||||
if len(o.WebhookOptions.ConfigFile) > 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("feature '%s' must be enabled to set option --audit-webhook-config-file", features.AdvancedAuditing))
|
||||
}
|
||||
} else {
|
||||
// check webhook mode
|
||||
validMode := false
|
||||
for _, m := range pluginwebhook.AllowedModes {
|
||||
if m == o.WebhookOptions.Mode {
|
||||
validMode = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validMode {
|
||||
allErrors = append(allErrors, fmt.Errorf("invalid audit webhook mode %s, allowed modes are %q", o.WebhookOptions.Mode, strings.Join(pluginwebhook.AllowedModes, ",")))
|
||||
}
|
||||
|
||||
// check log format
|
||||
validFormat := false
|
||||
for _, f := range pluginlog.AllowedFormats {
|
||||
if f == o.LogOptions.Format {
|
||||
validFormat = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !validFormat {
|
||||
allErrors = append(allErrors, fmt.Errorf("invalid audit log format %s, allowed formats are %q", o.LogOptions.Format, strings.Join(pluginlog.AllowedFormats, ",")))
|
||||
}
|
||||
}
|
||||
|
||||
// Check validities of MaxAge, MaxBackups and MaxSize of log options
|
||||
if o.LogOptions.MaxAge < 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--audit-log-maxage %v can't be a negative number", o.LogOptions.MaxAge))
|
||||
}
|
||||
if o.LogOptions.MaxBackups < 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--audit-log-maxbackup %v can't be a negative number", o.LogOptions.MaxBackups))
|
||||
}
|
||||
if o.LogOptions.MaxSize < 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--audit-log-maxsize %v can't be a negative number", o.LogOptions.MaxSize))
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func (o *AuditOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringVar(&o.PolicyFile, "audit-policy-file", o.PolicyFile,
|
||||
"Path to the file that defines the audit policy configuration. Requires the 'AdvancedAuditing' feature gate."+
|
||||
" With AdvancedAuditing, a profile is required to enable auditing.")
|
||||
|
||||
o.LogOptions.AddFlags(fs)
|
||||
o.WebhookOptions.AddFlags(fs)
|
||||
}
|
||||
|
||||
func (o *AuditOptions) ApplyTo(c *server.Config) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Apply legacy audit options if advanced audit is not enabled.
|
||||
if !advancedAuditingEnabled() {
|
||||
return o.LogOptions.legacyApplyTo(c)
|
||||
}
|
||||
|
||||
// Apply advanced options if advanced audit is enabled.
|
||||
// 1. Apply generic options.
|
||||
if err := o.applyTo(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Apply plugin options.
|
||||
if err := o.LogOptions.advancedApplyTo(c); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.WebhookOptions.applyTo(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if c.AuditBackend != nil && c.AuditPolicyChecker == nil {
|
||||
glog.V(2).Info("No audit policy file provided for AdvancedAuditing, no events will be recorded.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditOptions) applyTo(c *server.Config) error {
|
||||
if o.PolicyFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
p, err := policy.LoadPolicyFromFile(o.PolicyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("loading audit policy file: %v", err)
|
||||
}
|
||||
c.AuditPolicyChecker = policy.NewChecker(p)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&o.Path, "audit-log-path", o.Path,
|
||||
"If set, all requests coming to the apiserver will be logged to this file. '-' means standard out.")
|
||||
fs.IntVar(&o.MaxAge, "audit-log-maxage", o.MaxBackups,
|
||||
"The maximum number of days to retain old audit log files based on the timestamp encoded in their filename.")
|
||||
fs.IntVar(&o.MaxBackups, "audit-log-maxbackup", o.MaxBackups,
|
||||
"The maximum number of old audit log files to retain.")
|
||||
fs.IntVar(&o.MaxSize, "audit-log-maxsize", o.MaxSize,
|
||||
"The maximum size in megabytes of the audit log file before it gets rotated.")
|
||||
fs.StringVar(&o.Format, "audit-log-format", o.Format,
|
||||
"Format of saved audits. \"legacy\" indicates 1-line text format for each event."+
|
||||
" \"json\" indicates structured json format. Requires the 'AdvancedAuditing' feature"+
|
||||
" gate. Known formats are "+strings.Join(pluginlog.AllowedFormats, ",")+".")
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) getWriter() io.Writer {
|
||||
if o.Path == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var w io.Writer = os.Stdout
|
||||
if o.Path != "-" {
|
||||
w = &lumberjack.Logger{
|
||||
Filename: o.Path,
|
||||
MaxAge: o.MaxAge,
|
||||
MaxBackups: o.MaxBackups,
|
||||
MaxSize: o.MaxSize,
|
||||
}
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) advancedApplyTo(c *server.Config) error {
|
||||
if w := o.getWriter(); w != nil {
|
||||
c.AuditBackend = appendBackend(c.AuditBackend, pluginlog.NewBackend(w, o.Format, auditv1beta1.SchemeGroupVersion))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditLogOptions) legacyApplyTo(c *server.Config) error {
|
||||
c.LegacyAuditWriter = o.getWriter()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *AuditWebhookOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&o.ConfigFile, "audit-webhook-config-file", o.ConfigFile,
|
||||
"Path to a kubeconfig formatted file that defines the audit webhook configuration."+
|
||||
" Requires the 'AdvancedAuditing' feature gate.")
|
||||
fs.StringVar(&o.Mode, "audit-webhook-mode", o.Mode,
|
||||
"Strategy for sending audit events. Blocking indicates sending events should block"+
|
||||
" server responses. Batch causes the webhook to buffer and send events"+
|
||||
" asynchronously. Known modes are "+strings.Join(pluginwebhook.AllowedModes, ",")+".")
|
||||
}
|
||||
|
||||
func (o *AuditWebhookOptions) applyTo(c *server.Config) error {
|
||||
if o.ConfigFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
webhook, err := pluginwebhook.NewBackend(o.ConfigFile, o.Mode, auditv1beta1.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initializing audit webhook: %v", err)
|
||||
}
|
||||
c.AuditBackend = appendBackend(c.AuditBackend, webhook)
|
||||
return nil
|
||||
}
|
||||
388
vendor/k8s.io/apiserver/pkg/server/options/authentication.go
generated
vendored
Normal file
388
vendor/k8s.io/apiserver/pkg/server/options/authentication.go
generated
vendored
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/authentication/authenticatorfactory"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
|
||||
coreclient "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
type RequestHeaderAuthenticationOptions struct {
|
||||
UsernameHeaders []string
|
||||
GroupHeaders []string
|
||||
ExtraHeaderPrefixes []string
|
||||
ClientCAFile string
|
||||
AllowedNames []string
|
||||
}
|
||||
|
||||
func (s *RequestHeaderAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringSliceVar(&s.UsernameHeaders, "requestheader-username-headers", s.UsernameHeaders, ""+
|
||||
"List of request headers to inspect for usernames. X-Remote-User is common.")
|
||||
|
||||
fs.StringSliceVar(&s.GroupHeaders, "requestheader-group-headers", s.GroupHeaders, ""+
|
||||
"List of request headers to inspect for groups. X-Remote-Group is suggested.")
|
||||
|
||||
fs.StringSliceVar(&s.ExtraHeaderPrefixes, "requestheader-extra-headers-prefix", s.ExtraHeaderPrefixes, ""+
|
||||
"List of request header prefixes to inspect. X-Remote-Extra- is suggested.")
|
||||
|
||||
fs.StringVar(&s.ClientCAFile, "requestheader-client-ca-file", s.ClientCAFile, ""+
|
||||
"Root certificate bundle to use to verify client certificates on incoming requests "+
|
||||
"before trusting usernames in headers specified by --requestheader-username-headers")
|
||||
|
||||
fs.StringSliceVar(&s.AllowedNames, "requestheader-allowed-names", s.AllowedNames, ""+
|
||||
"List of client certificate common names to allow to provide usernames in headers "+
|
||||
"specified by --requestheader-username-headers. If empty, any client certificate validated "+
|
||||
"by the authorities in --requestheader-client-ca-file is allowed.")
|
||||
}
|
||||
|
||||
// ToAuthenticationRequestHeaderConfig returns a RequestHeaderConfig config object for these options
|
||||
// if necessary, nil otherwise.
|
||||
func (s *RequestHeaderAuthenticationOptions) ToAuthenticationRequestHeaderConfig() *authenticatorfactory.RequestHeaderConfig {
|
||||
if len(s.ClientCAFile) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &authenticatorfactory.RequestHeaderConfig{
|
||||
UsernameHeaders: s.UsernameHeaders,
|
||||
GroupHeaders: s.GroupHeaders,
|
||||
ExtraHeaderPrefixes: s.ExtraHeaderPrefixes,
|
||||
ClientCA: s.ClientCAFile,
|
||||
AllowedClientNames: s.AllowedNames,
|
||||
}
|
||||
}
|
||||
|
||||
type ClientCertAuthenticationOptions struct {
|
||||
// ClientCA is the certificate bundle for all the signers that you'll recognize for incoming client certificates
|
||||
ClientCA string
|
||||
}
|
||||
|
||||
func (s *ClientCertAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.ClientCA, "client-ca-file", s.ClientCA, ""+
|
||||
"If set, any request presenting a client certificate signed by one of "+
|
||||
"the authorities in the client-ca-file is authenticated with an identity "+
|
||||
"corresponding to the CommonName of the client certificate.")
|
||||
}
|
||||
|
||||
// DelegatingAuthenticationOptions provides an easy way for composing API servers to delegate their authentication to
|
||||
// the root kube API server. The API federator will act as
|
||||
// a front proxy and direction connections will be able to delegate to the core kube API server
|
||||
type DelegatingAuthenticationOptions struct {
|
||||
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
|
||||
// TokenAccessReview.authentication.k8s.io endpoint for checking tokens.
|
||||
RemoteKubeConfigFile string
|
||||
|
||||
// CacheTTL is the length of time that a token authentication answer will be cached.
|
||||
CacheTTL time.Duration
|
||||
|
||||
ClientCert ClientCertAuthenticationOptions
|
||||
RequestHeader RequestHeaderAuthenticationOptions
|
||||
|
||||
SkipInClusterLookup bool
|
||||
}
|
||||
|
||||
func NewDelegatingAuthenticationOptions() *DelegatingAuthenticationOptions {
|
||||
return &DelegatingAuthenticationOptions{
|
||||
// very low for responsiveness, but high enough to handle storms
|
||||
CacheTTL: 10 * time.Second,
|
||||
ClientCert: ClientCertAuthenticationOptions{},
|
||||
RequestHeader: RequestHeaderAuthenticationOptions{
|
||||
UsernameHeaders: []string{"x-remote-user"},
|
||||
GroupHeaders: []string{"x-remote-group"},
|
||||
ExtraHeaderPrefixes: []string{"x-remote-extra-"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) Validate() []error {
|
||||
allErrors := []error{}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.RemoteKubeConfigFile, "authentication-kubeconfig", s.RemoteKubeConfigFile, ""+
|
||||
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
|
||||
"tokenaccessreviews.authentication.k8s.io.")
|
||||
|
||||
fs.DurationVar(&s.CacheTTL, "authentication-token-webhook-cache-ttl", s.CacheTTL,
|
||||
"The duration to cache responses from the webhook token authenticator.")
|
||||
|
||||
s.ClientCert.AddFlags(fs)
|
||||
s.RequestHeader.AddFlags(fs)
|
||||
|
||||
fs.BoolVar(&s.SkipInClusterLookup, "authentication-skip-lookup", s.SkipInClusterLookup, ""+
|
||||
"If false, the authentication-kubeconfig will be used to lookup missing authentication "+
|
||||
"configuration from the cluster.")
|
||||
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.Config) error {
|
||||
if s == nil {
|
||||
c.Authenticator = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
clientCA, err := s.getClientCA()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err = c.ApplyClientCert(clientCA.ClientCA)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
|
||||
requestHeader, err := s.getRequestHeader()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c, err = c.ApplyClientCert(requestHeader.ClientCAFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
|
||||
cfg, err := s.ToAuthenticationConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authenticator, securityDefinitions, err := cfg.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Authenticator = authenticator
|
||||
if c.OpenAPIConfig != nil {
|
||||
c.OpenAPIConfig.SecurityDefinitions = securityDefinitions
|
||||
}
|
||||
c.SupportsBasicAuth = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) ToAuthenticationConfig() (authenticatorfactory.DelegatingAuthenticatorConfig, error) {
|
||||
tokenClient, err := s.newTokenAccessReview()
|
||||
if err != nil {
|
||||
return authenticatorfactory.DelegatingAuthenticatorConfig{}, err
|
||||
}
|
||||
|
||||
clientCA, err := s.getClientCA()
|
||||
if err != nil {
|
||||
return authenticatorfactory.DelegatingAuthenticatorConfig{}, err
|
||||
}
|
||||
requestHeader, err := s.getRequestHeader()
|
||||
if err != nil {
|
||||
return authenticatorfactory.DelegatingAuthenticatorConfig{}, err
|
||||
}
|
||||
|
||||
ret := authenticatorfactory.DelegatingAuthenticatorConfig{
|
||||
Anonymous: true,
|
||||
TokenAccessReviewClient: tokenClient,
|
||||
CacheTTL: s.CacheTTL,
|
||||
ClientCAFile: clientCA.ClientCA,
|
||||
RequestHeaderConfig: requestHeader.ToAuthenticationRequestHeaderConfig(),
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
const (
|
||||
authenticationConfigMapNamespace = metav1.NamespaceSystem
|
||||
authenticationConfigMapName = "extension-apiserver-authentication"
|
||||
authenticationRoleName = "extension-apiserver-authentication-reader"
|
||||
)
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) getClientCA() (*ClientCertAuthenticationOptions, error) {
|
||||
if len(s.ClientCert.ClientCA) > 0 || s.SkipInClusterLookup {
|
||||
return &s.ClientCert, nil
|
||||
}
|
||||
|
||||
incluster, err := s.lookupInClusterClientCA()
|
||||
if err != nil {
|
||||
glog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
|
||||
"'kubectl create rolebinding -n %s ROLE_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
|
||||
authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
|
||||
return nil, err
|
||||
}
|
||||
if incluster == nil {
|
||||
return nil, fmt.Errorf("cluster doesn't provide client-ca-file")
|
||||
}
|
||||
return incluster, nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) getRequestHeader() (*RequestHeaderAuthenticationOptions, error) {
|
||||
if len(s.RequestHeader.ClientCAFile) > 0 || s.SkipInClusterLookup {
|
||||
return &s.RequestHeader, nil
|
||||
}
|
||||
|
||||
incluster, err := s.lookupInClusterRequestHeader()
|
||||
if err != nil {
|
||||
glog.Warningf("Unable to get configmap/%s in %s. Usually fixed by "+
|
||||
"'kubectl create rolebinding -n %s ROLE_NAME --role=%s --serviceaccount=YOUR_NS:YOUR_SA'",
|
||||
authenticationConfigMapName, authenticationConfigMapNamespace, authenticationConfigMapNamespace, authenticationRoleName)
|
||||
return nil, err
|
||||
}
|
||||
if incluster == nil {
|
||||
return nil, fmt.Errorf("cluster doesn't provide requestheader-client-ca-file")
|
||||
}
|
||||
return incluster, nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) lookupInClusterClientCA() (*ClientCertAuthenticationOptions, error) {
|
||||
clientConfig, err := s.getClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := coreclient.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authConfigMap, err := client.ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clientCA, ok := authConfigMap.Data["client-ca-file"]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "client-ca-file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(clientCA), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ClientCertAuthenticationOptions{ClientCA: f.Name()}, nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) lookupInClusterRequestHeader() (*RequestHeaderAuthenticationOptions, error) {
|
||||
clientConfig, err := s.getClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := coreclient.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
authConfigMap, err := client.ConfigMaps(authenticationConfigMapNamespace).Get(authenticationConfigMapName, metav1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
requestHeaderCA, ok := authConfigMap.Data["requestheader-client-ca-file"]
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "requestheader-client-ca-file")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(requestHeaderCA), 0600); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
usernameHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-username-headers"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
groupHeaders, err := deserializeStrings(authConfigMap.Data["requestheader-group-headers"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
extraHeaderPrefixes, err := deserializeStrings(authConfigMap.Data["requestheader-extra-headers-prefix"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allowedNames, err := deserializeStrings(authConfigMap.Data["requestheader-allowed-names"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RequestHeaderAuthenticationOptions{
|
||||
UsernameHeaders: usernameHeaders,
|
||||
GroupHeaders: groupHeaders,
|
||||
ExtraHeaderPrefixes: extraHeaderPrefixes,
|
||||
ClientCAFile: f.Name(),
|
||||
AllowedNames: allowedNames,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func deserializeStrings(in string) ([]string, error) {
|
||||
if len(in) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var ret []string
|
||||
if err := json.Unmarshal([]byte(in), &ret); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) getClientConfig() (*rest.Config, error) {
|
||||
var clientConfig *rest.Config
|
||||
var err error
|
||||
if len(s.RemoteKubeConfigFile) > 0 {
|
||||
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
|
||||
clientConfig, err = loader.ClientConfig()
|
||||
|
||||
} else {
|
||||
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
|
||||
// use this path
|
||||
clientConfig, err = rest.InClusterConfig()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set high qps/burst limits since this will effectively limit API server responsiveness
|
||||
clientConfig.QPS = 200
|
||||
clientConfig.Burst = 400
|
||||
|
||||
return clientConfig, nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthenticationOptions) newTokenAccessReview() (authenticationclient.TokenReviewInterface, error) {
|
||||
clientConfig, err := s.getClientConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := authenticationclient.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.TokenReviews(), nil
|
||||
}
|
||||
138
vendor/k8s.io/apiserver/pkg/server/options/authorization.go
generated
vendored
Normal file
138
vendor/k8s.io/apiserver/pkg/server/options/authorization.go
generated
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apiserver/pkg/authorization/authorizerfactory"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1beta1"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// DelegatingAuthorizationOptions provides an easy way for composing API servers to delegate their authorization to
|
||||
// the root kube API server
|
||||
type DelegatingAuthorizationOptions struct {
|
||||
// RemoteKubeConfigFile is the file to use to connect to a "normal" kube API server which hosts the
|
||||
// SubjectAccessReview.authorization.k8s.io endpoint for checking tokens.
|
||||
RemoteKubeConfigFile string
|
||||
|
||||
// AllowCacheTTL is the length of time that a successful authorization response will be cached
|
||||
AllowCacheTTL time.Duration
|
||||
|
||||
// DenyCacheTTL is the length of time that an unsuccessful authorization response will be cached.
|
||||
// You generally want more responsive, "deny, try again" flows.
|
||||
DenyCacheTTL time.Duration
|
||||
}
|
||||
|
||||
func NewDelegatingAuthorizationOptions() *DelegatingAuthorizationOptions {
|
||||
return &DelegatingAuthorizationOptions{
|
||||
// very low for responsiveness, but high enough to handle storms
|
||||
AllowCacheTTL: 10 * time.Second,
|
||||
DenyCacheTTL: 10 * time.Second,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) Validate() []error {
|
||||
allErrors := []error{}
|
||||
return allErrors
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringVar(&s.RemoteKubeConfigFile, "authorization-kubeconfig", s.RemoteKubeConfigFile, ""+
|
||||
"kubeconfig file pointing at the 'core' kubernetes server with enough rights to create "+
|
||||
" subjectaccessreviews.authorization.k8s.io.")
|
||||
|
||||
fs.DurationVar(&s.AllowCacheTTL, "authorization-webhook-cache-authorized-ttl",
|
||||
s.AllowCacheTTL,
|
||||
"The duration to cache 'authorized' responses from the webhook authorizer.")
|
||||
|
||||
fs.DurationVar(&s.DenyCacheTTL,
|
||||
"authorization-webhook-cache-unauthorized-ttl", s.DenyCacheTTL,
|
||||
"The duration to cache 'unauthorized' responses from the webhook authorizer.")
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) ApplyTo(c *server.Config) error {
|
||||
if s == nil {
|
||||
c.Authorizer = authorizerfactory.NewAlwaysAllowAuthorizer()
|
||||
return nil
|
||||
}
|
||||
|
||||
cfg, err := s.ToAuthorizationConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
authorizer, err := cfg.New()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Authorizer = authorizer
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) ToAuthorizationConfig() (authorizerfactory.DelegatingAuthorizerConfig, error) {
|
||||
sarClient, err := s.newSubjectAccessReview()
|
||||
if err != nil {
|
||||
return authorizerfactory.DelegatingAuthorizerConfig{}, err
|
||||
}
|
||||
|
||||
ret := authorizerfactory.DelegatingAuthorizerConfig{
|
||||
SubjectAccessReviewClient: sarClient,
|
||||
AllowCacheTTL: s.AllowCacheTTL,
|
||||
DenyCacheTTL: s.DenyCacheTTL,
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (s *DelegatingAuthorizationOptions) newSubjectAccessReview() (authorizationclient.SubjectAccessReviewInterface, error) {
|
||||
var clientConfig *rest.Config
|
||||
var err error
|
||||
if len(s.RemoteKubeConfigFile) > 0 {
|
||||
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: s.RemoteKubeConfigFile}
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
|
||||
clientConfig, err = loader.ClientConfig()
|
||||
|
||||
} else {
|
||||
// without the remote kubeconfig file, try to use the in-cluster config. Most addon API servers will
|
||||
// use this path
|
||||
clientConfig, err = rest.InClusterConfig()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// set high qps/burst limits since this will effectively limit API server responsiveness
|
||||
clientConfig.QPS = 200
|
||||
clientConfig.Burst = 400
|
||||
|
||||
client, err := authorizationclient.NewForConfig(clientConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return client.SubjectAccessReviews(), nil
|
||||
}
|
||||
83
vendor/k8s.io/apiserver/pkg/server/options/coreapi.go
generated
vendored
Normal file
83
vendor/k8s.io/apiserver/pkg/server/options/coreapi.go
generated
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
clientgoinformers "k8s.io/client-go/informers"
|
||||
clientgoclientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
)
|
||||
|
||||
// CoreAPIOptions contains options to configure the connection to a core API Kubernetes apiserver.
|
||||
type CoreAPIOptions struct {
|
||||
// CoreAPIKubeconfigPath is a filename for a kubeconfig file to contact the core API server with.
|
||||
// If it is not set, the in cluster config is used.
|
||||
CoreAPIKubeconfigPath string
|
||||
}
|
||||
|
||||
func NewCoreAPIOptions() *CoreAPIOptions {
|
||||
return &CoreAPIOptions{}
|
||||
}
|
||||
|
||||
func (o *CoreAPIOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringVar(&o.CoreAPIKubeconfigPath, "kubeconfig", o.CoreAPIKubeconfigPath,
|
||||
"kubeconfig file pointing at the 'core' kubernetes server.")
|
||||
}
|
||||
|
||||
func (o *CoreAPIOptions) ApplyTo(config *server.RecommendedConfig) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// create shared informer for Kubernetes APIs
|
||||
var kubeconfig *rest.Config
|
||||
var err error
|
||||
if len(o.CoreAPIKubeconfigPath) > 0 {
|
||||
loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: o.CoreAPIKubeconfigPath}
|
||||
loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{})
|
||||
kubeconfig, err = loader.ClientConfig()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load kubeconfig at %q: %v", o.CoreAPIKubeconfigPath, err)
|
||||
}
|
||||
} else {
|
||||
kubeconfig, err = rest.InClusterConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
clientgoExternalClient, err := clientgoclientset.NewForConfig(kubeconfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Kubernetes clientset: %v", err)
|
||||
}
|
||||
config.SharedInformerFactory = clientgoinformers.NewSharedInformerFactory(clientgoExternalClient, 10*time.Minute)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *CoreAPIOptions) Validate() []error {
|
||||
return nil
|
||||
}
|
||||
21
vendor/k8s.io/apiserver/pkg/server/options/doc.go
generated
vendored
Normal file
21
vendor/k8s.io/apiserver/pkg/server/options/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
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 options is the public flags and options used by a generic api
|
||||
// server. It takes a minimal set of dependencies and does not reference
|
||||
// implementations, in order to ensure it may be reused by multiple components
|
||||
// (such as CLI commands that wish to generate or validate config).
|
||||
package options // import "k8s.io/apiserver/pkg/server/options"
|
||||
339
vendor/k8s.io/apiserver/pkg/server/options/encryptionconfig/encryptionconfig_test.go
generated
vendored
Normal file
339
vendor/k8s.io/apiserver/pkg/server/options/encryptionconfig/encryptionconfig_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
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 encryptionconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/storage/value"
|
||||
"k8s.io/apiserver/pkg/storage/value/encrypt/envelope"
|
||||
)
|
||||
|
||||
const (
|
||||
sampleText = "abcdefghijklmnopqrstuvwxyz"
|
||||
|
||||
sampleContextText = "0123456789"
|
||||
|
||||
// Modify these in all configurations if changed
|
||||
testEnvelopeServiceConfigPath = "testproviderconfig"
|
||||
testEnvelopeServiceProviderName = "testprovider"
|
||||
|
||||
correctConfigWithIdentityFirst = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
- namespaces
|
||||
providers:
|
||||
- identity: {}
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
- kms:
|
||||
name: testprovider
|
||||
configfile: testproviderconfig
|
||||
cachesize: 10
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
- secretbox:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||
`
|
||||
|
||||
correctConfigWithAesGcmFirst = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
- secretbox:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||
- kms:
|
||||
name: testprovider
|
||||
configfile: testproviderconfig
|
||||
cachesize: 10
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
- identity: {}
|
||||
`
|
||||
|
||||
correctConfigWithAesCbcFirst = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
- kms:
|
||||
name: testprovider
|
||||
configfile: testproviderconfig
|
||||
cachesize: 10
|
||||
- identity: {}
|
||||
- secretbox:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
`
|
||||
|
||||
correctConfigWithSecretboxFirst = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- secretbox:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
- kms:
|
||||
name: testprovider
|
||||
configfile: testproviderconfig
|
||||
cachesize: 10
|
||||
- identity: {}
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
`
|
||||
|
||||
correctConfigWithKMSFirst = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- secrets
|
||||
providers:
|
||||
- kms:
|
||||
name: testprovider
|
||||
configfile: testproviderconfig
|
||||
cachesize: 10
|
||||
- secretbox:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
|
||||
- aescbc:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
- identity: {}
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: dGhpcyBpcyBwYXNzd29yZA==
|
||||
`
|
||||
|
||||
incorrectConfigNoSecretForKey = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- namespaces
|
||||
- secrets
|
||||
providers:
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
`
|
||||
|
||||
incorrectConfigInvalidKey = `
|
||||
kind: EncryptionConfig
|
||||
apiVersion: v1
|
||||
resources:
|
||||
- resources:
|
||||
- namespaces
|
||||
- secrets
|
||||
providers:
|
||||
- aesgcm:
|
||||
keys:
|
||||
- name: key1
|
||||
secret: c2VjcmV0IGlzIHNlY3VyZQ==
|
||||
- name: key2
|
||||
secret: YSBzZWNyZXQgYSBzZWNyZXQ=
|
||||
`
|
||||
)
|
||||
|
||||
// testEnvelopeService is a mock envelope service which can be used to simulate remote Envelope services
|
||||
// for testing of the envelope transformer with other transformers.
|
||||
type testEnvelopeService struct {
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (t *testEnvelopeService) Decrypt(data string) ([]byte, error) {
|
||||
if t.disabled {
|
||||
return nil, fmt.Errorf("Envelope service was disabled")
|
||||
}
|
||||
return base64.StdEncoding.DecodeString(data)
|
||||
}
|
||||
|
||||
func (t *testEnvelopeService) Encrypt(data []byte) (string, error) {
|
||||
if t.disabled {
|
||||
return "", fmt.Errorf("Envelope service was disabled")
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(data), nil
|
||||
}
|
||||
|
||||
func (t *testEnvelopeService) SetDisabledStatus(status bool) {
|
||||
t.disabled = status
|
||||
}
|
||||
|
||||
var _ envelope.Service = &testEnvelopeService{}
|
||||
|
||||
func TestEncryptionProviderConfigCorrect(t *testing.T) {
|
||||
os.OpenFile(testEnvelopeServiceConfigPath, os.O_CREATE, 0666)
|
||||
defer os.Remove(testEnvelopeServiceConfigPath)
|
||||
KMSPluginRegistry.Register(testEnvelopeServiceProviderName, func(_ io.Reader) (envelope.Service, error) {
|
||||
return &testEnvelopeService{}, nil
|
||||
})
|
||||
|
||||
// Creates compound/prefix transformers with different ordering of available transformers.
|
||||
// Transforms data using one of them, and tries to untransform using the others.
|
||||
// Repeats this for all possible combinations.
|
||||
identityFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithIdentityFirst))
|
||||
if err != nil {
|
||||
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithIdentityFirst)
|
||||
}
|
||||
|
||||
aesGcmFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesGcmFirst))
|
||||
if err != nil {
|
||||
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesGcmFirst)
|
||||
}
|
||||
|
||||
aesCbcFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithAesCbcFirst))
|
||||
if err != nil {
|
||||
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithAesCbcFirst)
|
||||
}
|
||||
|
||||
secretboxFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithSecretboxFirst))
|
||||
if err != nil {
|
||||
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithSecretboxFirst)
|
||||
}
|
||||
|
||||
kmsFirstTransformerOverrides, err := ParseEncryptionConfiguration(strings.NewReader(correctConfigWithKMSFirst))
|
||||
if err != nil {
|
||||
t.Fatalf("error while parsing configuration file: %s.\nThe file was:\n%s", err, correctConfigWithKMSFirst)
|
||||
}
|
||||
|
||||
// Pick the transformer for any of the returned resources.
|
||||
identityFirstTransformer := identityFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
|
||||
aesGcmFirstTransformer := aesGcmFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
|
||||
aesCbcFirstTransformer := aesCbcFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
|
||||
secretboxFirstTransformer := secretboxFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
|
||||
kmsFirstTransformer := kmsFirstTransformerOverrides[schema.ParseGroupResource("secrets")]
|
||||
|
||||
context := value.DefaultContext([]byte(sampleContextText))
|
||||
originalText := []byte(sampleText)
|
||||
|
||||
transformers := []struct {
|
||||
Transformer value.Transformer
|
||||
Name string
|
||||
}{
|
||||
{aesGcmFirstTransformer, "aesGcmFirst"},
|
||||
{aesCbcFirstTransformer, "aesCbcFirst"},
|
||||
{secretboxFirstTransformer, "secretboxFirst"},
|
||||
{identityFirstTransformer, "identityFirst"},
|
||||
{kmsFirstTransformer, "kmsFirst"},
|
||||
}
|
||||
|
||||
for _, testCase := range transformers {
|
||||
transformedData, err := testCase.Transformer.TransformToStorage(originalText, context)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error while transforming data to storage: %s", testCase.Name, err)
|
||||
}
|
||||
|
||||
for _, transformer := range transformers {
|
||||
untransformedData, stale, err := transformer.Transformer.TransformFromStorage(transformedData, context)
|
||||
if err != nil {
|
||||
t.Fatalf("%s: error while reading using %s transformer: %s", testCase.Name, transformer.Name, err)
|
||||
}
|
||||
if stale != (transformer.Name != testCase.Name) {
|
||||
t.Fatalf("%s: wrong stale information on reading using %s transformer, should be %v", testCase.Name, transformer.Name, testCase.Name == transformer.Name)
|
||||
}
|
||||
if bytes.Compare(untransformedData, originalText) != 0 {
|
||||
t.Fatalf("%s: %s transformer transformed data incorrectly. Expected: %v, got %v", testCase.Name, transformer.Name, originalText, untransformedData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Throw error if key has no secret
|
||||
func TestEncryptionProviderConfigNoSecretForKey(t *testing.T) {
|
||||
if _, err := ParseEncryptionConfiguration(strings.NewReader(incorrectConfigNoSecretForKey)); err == nil {
|
||||
t.Fatalf("invalid configuration file (one key has no secret) got parsed:\n%s", incorrectConfigNoSecretForKey)
|
||||
}
|
||||
}
|
||||
|
||||
// Throw error if invalid key for AES
|
||||
func TestEncryptionProviderConfigInvalidKey(t *testing.T) {
|
||||
if _, err := ParseEncryptionConfiguration(strings.NewReader(incorrectConfigInvalidKey)); err == nil {
|
||||
t.Fatalf("invalid configuration file (bad AES key) got parsed:\n%s", incorrectConfigInvalidKey)
|
||||
}
|
||||
}
|
||||
275
vendor/k8s.io/apiserver/pkg/server/options/etcd.go
generated
vendored
Normal file
275
vendor/k8s.io/apiserver/pkg/server/options/etcd.go
generated
vendored
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/registry/generic"
|
||||
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/healthz"
|
||||
serverstorage "k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/apiserver/pkg/storage/etcd3/preflight"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
)
|
||||
|
||||
type EtcdOptions struct {
|
||||
// The value of Paging on StorageConfig will be overriden by the
|
||||
// calculated feature gate value.
|
||||
StorageConfig storagebackend.Config
|
||||
EncryptionProviderConfigFilepath string
|
||||
|
||||
EtcdServersOverrides []string
|
||||
|
||||
// To enable protobuf as storage format, it is enough
|
||||
// to set it to "application/vnd.kubernetes.protobuf".
|
||||
DefaultStorageMediaType string
|
||||
DeleteCollectionWorkers int
|
||||
EnableGarbageCollection bool
|
||||
|
||||
// Set EnableWatchCache to false to disable all watch caches
|
||||
EnableWatchCache bool
|
||||
// Set DefaultWatchCacheSize to zero to disable watch caches for those resources that have no explicit cache size set
|
||||
DefaultWatchCacheSize int
|
||||
// WatchCacheSizes represents override to a given resource
|
||||
WatchCacheSizes []string
|
||||
}
|
||||
|
||||
var storageTypes = sets.NewString(
|
||||
storagebackend.StorageTypeUnset,
|
||||
storagebackend.StorageTypeETCD2,
|
||||
storagebackend.StorageTypeETCD3,
|
||||
)
|
||||
|
||||
func NewEtcdOptions(backendConfig *storagebackend.Config) *EtcdOptions {
|
||||
return &EtcdOptions{
|
||||
StorageConfig: *backendConfig,
|
||||
DefaultStorageMediaType: "application/json",
|
||||
DeleteCollectionWorkers: 1,
|
||||
EnableGarbageCollection: true,
|
||||
EnableWatchCache: true,
|
||||
DefaultWatchCacheSize: 100,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) Validate() []error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
allErrors := []error{}
|
||||
if len(s.StorageConfig.ServerList) == 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("--etcd-servers must be specified"))
|
||||
}
|
||||
|
||||
if !storageTypes.Has(s.StorageConfig.Type) {
|
||||
allErrors = append(allErrors, fmt.Errorf("--storage-backend invalid, must be 'etcd3' or 'etcd2'. If not specified, it will default to 'etcd3'"))
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// AddEtcdFlags adds flags related to etcd storage for a specific APIServer to the specified FlagSet
|
||||
func (s *EtcdOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringSliceVar(&s.EtcdServersOverrides, "etcd-servers-overrides", s.EtcdServersOverrides, ""+
|
||||
"Per-resource etcd servers overrides, comma separated. The individual override "+
|
||||
"format: group/resource#servers, where servers are http://ip:port, semicolon separated.")
|
||||
|
||||
fs.StringVar(&s.DefaultStorageMediaType, "storage-media-type", s.DefaultStorageMediaType, ""+
|
||||
"The media type to use to store objects in storage. "+
|
||||
"Some resources or storage backends may only support a specific media type and will ignore this setting.")
|
||||
fs.IntVar(&s.DeleteCollectionWorkers, "delete-collection-workers", s.DeleteCollectionWorkers,
|
||||
"Number of workers spawned for DeleteCollection call. These are used to speed up namespace cleanup.")
|
||||
|
||||
fs.BoolVar(&s.EnableGarbageCollection, "enable-garbage-collector", s.EnableGarbageCollection, ""+
|
||||
"Enables the generic garbage collector. MUST be synced with the corresponding flag "+
|
||||
"of the kube-controller-manager.")
|
||||
|
||||
fs.BoolVar(&s.EnableWatchCache, "watch-cache", s.EnableWatchCache,
|
||||
"Enable watch caching in the apiserver")
|
||||
|
||||
fs.IntVar(&s.DefaultWatchCacheSize, "default-watch-cache-size", s.DefaultWatchCacheSize,
|
||||
"Default watch cache size. If zero, watch cache will be disabled for resources that do not have a default watch size set.")
|
||||
|
||||
fs.StringSliceVar(&s.WatchCacheSizes, "watch-cache-sizes", s.WatchCacheSizes, ""+
|
||||
"List of watch cache sizes for every resource (pods, nodes, etc.), comma separated. "+
|
||||
"The individual override format: resource#size, where size is a number. It takes effect "+
|
||||
"when watch-cache is enabled.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.Type, "storage-backend", s.StorageConfig.Type,
|
||||
"The storage backend for persistence. Options: 'etcd3' (default), 'etcd2'.")
|
||||
|
||||
fs.IntVar(&s.StorageConfig.DeserializationCacheSize, "deserialization-cache-size", s.StorageConfig.DeserializationCacheSize,
|
||||
"Number of deserialized json objects to cache in memory.")
|
||||
|
||||
fs.StringSliceVar(&s.StorageConfig.ServerList, "etcd-servers", s.StorageConfig.ServerList,
|
||||
"List of etcd servers to connect with (scheme://ip:port), comma separated.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.Prefix, "etcd-prefix", s.StorageConfig.Prefix,
|
||||
"The prefix to prepend to all resource paths in etcd.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.KeyFile, "etcd-keyfile", s.StorageConfig.KeyFile,
|
||||
"SSL key file used to secure etcd communication.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.CertFile, "etcd-certfile", s.StorageConfig.CertFile,
|
||||
"SSL certification file used to secure etcd communication.")
|
||||
|
||||
fs.StringVar(&s.StorageConfig.CAFile, "etcd-cafile", s.StorageConfig.CAFile,
|
||||
"SSL Certificate Authority file used to secure etcd communication.")
|
||||
|
||||
fs.BoolVar(&s.StorageConfig.Quorum, "etcd-quorum-read", s.StorageConfig.Quorum,
|
||||
"If true, enable quorum read.")
|
||||
|
||||
fs.StringVar(&s.EncryptionProviderConfigFilepath, "experimental-encryption-provider-config", s.EncryptionProviderConfigFilepath,
|
||||
"The file containing configuration for encryption providers to be used for storing secrets in etcd")
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) ApplyTo(c *server.Config) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.addEtcdHealthEndpoint(c)
|
||||
c.RESTOptionsGetter = &SimpleRestOptionsFactory{Options: *s}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) ApplyWithStorageFactoryTo(factory serverstorage.StorageFactory, c *server.Config) error {
|
||||
s.addEtcdHealthEndpoint(c)
|
||||
c.RESTOptionsGetter = &storageFactoryRestOptionsFactory{Options: *s, StorageFactory: factory}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *EtcdOptions) addEtcdHealthEndpoint(c *server.Config) {
|
||||
c.HealthzChecks = append(c.HealthzChecks, healthz.NamedCheck("etcd", func(r *http.Request) error {
|
||||
done, err := preflight.EtcdConnection{ServerList: s.StorageConfig.ServerList}.CheckEtcdServers()
|
||||
if !done {
|
||||
return fmt.Errorf("etcd failed")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}))
|
||||
}
|
||||
|
||||
type SimpleRestOptionsFactory struct {
|
||||
Options EtcdOptions
|
||||
}
|
||||
|
||||
func (f *SimpleRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
||||
ret := generic.RESTOptions{
|
||||
StorageConfig: &f.Options.StorageConfig,
|
||||
Decorator: generic.UndecoratedStorage,
|
||||
EnableGarbageCollection: f.Options.EnableGarbageCollection,
|
||||
DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
|
||||
ResourcePrefix: resource.Group + "/" + resource.Resource,
|
||||
}
|
||||
if f.Options.EnableWatchCache {
|
||||
sizes, err := ParseWatchCacheSizes(f.Options.WatchCacheSizes)
|
||||
if err != nil {
|
||||
return generic.RESTOptions{}, err
|
||||
}
|
||||
cacheSize, ok := sizes[resource]
|
||||
if !ok {
|
||||
cacheSize = f.Options.DefaultWatchCacheSize
|
||||
}
|
||||
ret.Decorator = genericregistry.StorageWithCacher(cacheSize)
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
type storageFactoryRestOptionsFactory struct {
|
||||
Options EtcdOptions
|
||||
StorageFactory serverstorage.StorageFactory
|
||||
}
|
||||
|
||||
func (f *storageFactoryRestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
|
||||
storageConfig, err := f.StorageFactory.NewConfig(resource)
|
||||
if err != nil {
|
||||
return generic.RESTOptions{}, fmt.Errorf("unable to find storage destination for %v, due to %v", resource, err.Error())
|
||||
}
|
||||
|
||||
ret := generic.RESTOptions{
|
||||
StorageConfig: storageConfig,
|
||||
Decorator: generic.UndecoratedStorage,
|
||||
DeleteCollectionWorkers: f.Options.DeleteCollectionWorkers,
|
||||
EnableGarbageCollection: f.Options.EnableGarbageCollection,
|
||||
ResourcePrefix: f.StorageFactory.ResourcePrefix(resource),
|
||||
}
|
||||
if f.Options.EnableWatchCache {
|
||||
sizes, err := ParseWatchCacheSizes(f.Options.WatchCacheSizes)
|
||||
if err != nil {
|
||||
return generic.RESTOptions{}, err
|
||||
}
|
||||
cacheSize, ok := sizes[resource]
|
||||
if !ok {
|
||||
cacheSize = f.Options.DefaultWatchCacheSize
|
||||
}
|
||||
ret.Decorator = genericregistry.StorageWithCacher(cacheSize)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ParseWatchCacheSizes turns a list of cache size values into a map of group resources
|
||||
// to requested sizes.
|
||||
func ParseWatchCacheSizes(cacheSizes []string) (map[schema.GroupResource]int, error) {
|
||||
watchCacheSizes := make(map[schema.GroupResource]int)
|
||||
for _, c := range cacheSizes {
|
||||
tokens := strings.Split(c, "#")
|
||||
if len(tokens) != 2 {
|
||||
return nil, fmt.Errorf("invalid value of watch cache size: %s", c)
|
||||
}
|
||||
|
||||
size, err := strconv.Atoi(tokens[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid size of watch cache size: %s", c)
|
||||
}
|
||||
if size < 0 {
|
||||
return nil, fmt.Errorf("watch cache size cannot be negative: %s", c)
|
||||
}
|
||||
|
||||
watchCacheSizes[schema.ParseGroupResource(tokens[0])] = size
|
||||
}
|
||||
return watchCacheSizes, nil
|
||||
}
|
||||
|
||||
// WriteWatchCacheSizes turns a map of cache size values into a list of string specifications.
|
||||
func WriteWatchCacheSizes(watchCacheSizes map[schema.GroupResource]int) ([]string, error) {
|
||||
var cacheSizes []string
|
||||
|
||||
for resource, size := range watchCacheSizes {
|
||||
if size < 0 {
|
||||
return nil, fmt.Errorf("watch cache size cannot be negative for resource %s", resource)
|
||||
}
|
||||
cacheSizes = append(cacheSizes, fmt.Sprintf("%s#%d", resource.String(), size))
|
||||
}
|
||||
return cacheSizes, nil
|
||||
}
|
||||
74
vendor/k8s.io/apiserver/pkg/server/options/feature.go
generated
vendored
Normal file
74
vendor/k8s.io/apiserver/pkg/server/options/feature.go
generated
vendored
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
)
|
||||
|
||||
type FeatureOptions struct {
|
||||
EnableProfiling bool
|
||||
EnableContentionProfiling bool
|
||||
EnableSwaggerUI bool
|
||||
}
|
||||
|
||||
func NewFeatureOptions() *FeatureOptions {
|
||||
defaults := server.NewConfig(serializer.CodecFactory{})
|
||||
|
||||
return &FeatureOptions{
|
||||
EnableProfiling: defaults.EnableProfiling,
|
||||
EnableContentionProfiling: defaults.EnableContentionProfiling,
|
||||
EnableSwaggerUI: defaults.EnableSwaggerUI,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *FeatureOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.BoolVar(&o.EnableProfiling, "profiling", o.EnableProfiling,
|
||||
"Enable profiling via web interface host:port/debug/pprof/")
|
||||
fs.BoolVar(&o.EnableContentionProfiling, "contention-profiling", o.EnableContentionProfiling,
|
||||
"Enable lock contention profiling, if profiling is enabled")
|
||||
fs.BoolVar(&o.EnableSwaggerUI, "enable-swagger-ui", o.EnableSwaggerUI,
|
||||
"Enables swagger ui on the apiserver at /swagger-ui")
|
||||
}
|
||||
|
||||
func (o *FeatureOptions) ApplyTo(c *server.Config) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.EnableProfiling = o.EnableProfiling
|
||||
c.EnableContentionProfiling = o.EnableContentionProfiling
|
||||
c.EnableSwaggerUI = o.EnableSwaggerUI
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *FeatureOptions) Validate() []error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
return errs
|
||||
}
|
||||
98
vendor/k8s.io/apiserver/pkg/server/options/recommended.go
generated
vendored
Normal file
98
vendor/k8s.io/apiserver/pkg/server/options/recommended.go
generated
vendored
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
)
|
||||
|
||||
// RecommendedOptions contains the recommended options for running an API server.
|
||||
// If you add something to this list, it should be in a logical grouping.
|
||||
// Each of them can be nil to leave the feature unconfigured on ApplyTo.
|
||||
type RecommendedOptions struct {
|
||||
Etcd *EtcdOptions
|
||||
SecureServing *SecureServingOptions
|
||||
Authentication *DelegatingAuthenticationOptions
|
||||
Authorization *DelegatingAuthorizationOptions
|
||||
Audit *AuditOptions
|
||||
Features *FeatureOptions
|
||||
CoreAPI *CoreAPIOptions
|
||||
}
|
||||
|
||||
func NewRecommendedOptions(prefix string, copier runtime.ObjectCopier, codec runtime.Codec) *RecommendedOptions {
|
||||
return &RecommendedOptions{
|
||||
Etcd: NewEtcdOptions(storagebackend.NewDefaultConfig(prefix, copier, codec)),
|
||||
SecureServing: NewSecureServingOptions(),
|
||||
Authentication: NewDelegatingAuthenticationOptions(),
|
||||
Authorization: NewDelegatingAuthorizationOptions(),
|
||||
Audit: NewAuditOptions(),
|
||||
Features: NewFeatureOptions(),
|
||||
CoreAPI: NewCoreAPIOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
func (o *RecommendedOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
o.Etcd.AddFlags(fs)
|
||||
o.SecureServing.AddFlags(fs)
|
||||
o.Authentication.AddFlags(fs)
|
||||
o.Authorization.AddFlags(fs)
|
||||
o.Audit.AddFlags(fs)
|
||||
o.Features.AddFlags(fs)
|
||||
o.CoreAPI.AddFlags(fs)
|
||||
}
|
||||
|
||||
func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig) error {
|
||||
if err := o.Etcd.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.SecureServing.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Authentication.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Authorization.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Audit.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Features.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.CoreAPI.ApplyTo(config); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o *RecommendedOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
errors = append(errors, o.Etcd.Validate()...)
|
||||
errors = append(errors, o.SecureServing.Validate()...)
|
||||
errors = append(errors, o.Authentication.Validate()...)
|
||||
errors = append(errors, o.Authorization.Validate()...)
|
||||
errors = append(errors, o.Audit.Validate()...)
|
||||
errors = append(errors, o.Features.Validate()...)
|
||||
errors = append(errors, o.CoreAPI.Validate()...)
|
||||
|
||||
return errors
|
||||
}
|
||||
154
vendor/k8s.io/apiserver/pkg/server/options/server_run_options.go
generated
vendored
Normal file
154
vendor/k8s.io/apiserver/pkg/server/options/server_run_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
|
||||
// add the generic feature gates
|
||||
_ "k8s.io/apiserver/pkg/features"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// ServerRunOptions contains the options while running a generic api server.
|
||||
type ServerRunOptions struct {
|
||||
AdvertiseAddress net.IP
|
||||
|
||||
CorsAllowedOriginList []string
|
||||
ExternalHost string
|
||||
MaxRequestsInFlight int
|
||||
MaxMutatingRequestsInFlight int
|
||||
RequestTimeout time.Duration
|
||||
MinRequestTimeout int
|
||||
TargetRAMMB int
|
||||
}
|
||||
|
||||
func NewServerRunOptions() *ServerRunOptions {
|
||||
defaults := server.NewConfig(serializer.CodecFactory{})
|
||||
return &ServerRunOptions{
|
||||
MaxRequestsInFlight: defaults.MaxRequestsInFlight,
|
||||
MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight,
|
||||
RequestTimeout: defaults.RequestTimeout,
|
||||
MinRequestTimeout: defaults.MinRequestTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// ApplyOptions applies the run options to the method receiver and returns self
|
||||
func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
|
||||
c.CorsAllowedOriginList = s.CorsAllowedOriginList
|
||||
c.ExternalAddress = s.ExternalHost
|
||||
c.MaxRequestsInFlight = s.MaxRequestsInFlight
|
||||
c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
|
||||
c.RequestTimeout = s.RequestTimeout
|
||||
c.MinRequestTimeout = s.MinRequestTimeout
|
||||
c.PublicAddress = s.AdvertiseAddress
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DefaultAdvertiseAddress sets the field AdvertiseAddress if unset. The field will be set based on the SecureServingOptions.
|
||||
func (s *ServerRunOptions) DefaultAdvertiseAddress(secure *SecureServingOptions) error {
|
||||
if secure == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
|
||||
hostIP, err := secure.DefaultExternalAddress()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to find suitable network address.error='%v'. "+
|
||||
"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err)
|
||||
}
|
||||
s.AdvertiseAddress = hostIP
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate checks validation of ServerRunOptions
|
||||
func (s *ServerRunOptions) Validate() []error {
|
||||
errors := []error{}
|
||||
if s.TargetRAMMB < 0 {
|
||||
errors = append(errors, fmt.Errorf("--target-ram-mb can not be negative value"))
|
||||
}
|
||||
if s.MaxRequestsInFlight < 0 {
|
||||
errors = append(errors, fmt.Errorf("--max-requests-inflight can not be negative value"))
|
||||
}
|
||||
if s.MaxMutatingRequestsInFlight < 0 {
|
||||
errors = append(errors, fmt.Errorf("--max-mutating-requests-inflight can not be negative value"))
|
||||
}
|
||||
|
||||
if s.RequestTimeout.Nanoseconds() < 0 {
|
||||
errors = append(errors, fmt.Errorf("--request-timeout can not be negative value"))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
|
||||
func (s *ServerRunOptions) AddUniversalFlags(fs *pflag.FlagSet) {
|
||||
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||
// arrange these text blocks sensibly. Grrr.
|
||||
|
||||
fs.IPVar(&s.AdvertiseAddress, "advertise-address", s.AdvertiseAddress, ""+
|
||||
"The IP address on which to advertise the apiserver to members of the cluster. This "+
|
||||
"address must be reachable by the rest of the cluster. If blank, the --bind-address "+
|
||||
"will be used. If --bind-address is unspecified, the host's default interface will "+
|
||||
"be used.")
|
||||
|
||||
fs.StringSliceVar(&s.CorsAllowedOriginList, "cors-allowed-origins", s.CorsAllowedOriginList, ""+
|
||||
"List of allowed origins for CORS, comma separated. An allowed origin can be a regular "+
|
||||
"expression to support subdomain matching. If this list is empty CORS will not be enabled.")
|
||||
|
||||
fs.IntVar(&s.TargetRAMMB, "target-ram-mb", s.TargetRAMMB,
|
||||
"Memory limit for apiserver in MB (used to configure sizes of caches, etc.)")
|
||||
|
||||
fs.StringVar(&s.ExternalHost, "external-hostname", s.ExternalHost,
|
||||
"The hostname to use when generating externalized URLs for this master (e.g. Swagger API Docs).")
|
||||
|
||||
deprecatedMasterServiceNamespace := metav1.NamespaceDefault
|
||||
fs.StringVar(&deprecatedMasterServiceNamespace, "master-service-namespace", deprecatedMasterServiceNamespace, ""+
|
||||
"DEPRECATED: the namespace from which the kubernetes master services should be injected into pods.")
|
||||
|
||||
fs.IntVar(&s.MaxRequestsInFlight, "max-requests-inflight", s.MaxRequestsInFlight, ""+
|
||||
"The maximum number of non-mutating requests in flight at a given time. When the server exceeds this, "+
|
||||
"it rejects requests. Zero for no limit.")
|
||||
|
||||
fs.IntVar(&s.MaxMutatingRequestsInFlight, "max-mutating-requests-inflight", s.MaxMutatingRequestsInFlight, ""+
|
||||
"The maximum number of mutating requests in flight at a given time. When the server exceeds this, "+
|
||||
"it rejects requests. Zero for no limit.")
|
||||
|
||||
fs.DurationVar(&s.RequestTimeout, "request-timeout", s.RequestTimeout, ""+
|
||||
"An optional field indicating the duration a handler must keep a request open before timing "+
|
||||
"it out. This is the default request timeout for requests but may be overridden by flags such as "+
|
||||
"--min-request-timeout for specific types of requests.")
|
||||
|
||||
fs.IntVar(&s.MinRequestTimeout, "min-request-timeout", s.MinRequestTimeout, ""+
|
||||
"An optional field indicating the minimum number of seconds a handler must keep "+
|
||||
"a request open before timing it out. Currently only honored by the watch request "+
|
||||
"handler, which picks a randomized value above this number as the connection timeout, "+
|
||||
"to spread out load.")
|
||||
|
||||
utilfeature.DefaultFeatureGate.AddFlag(fs)
|
||||
}
|
||||
288
vendor/k8s.io/apiserver/pkg/server/options/serving.go
generated
vendored
Normal file
288
vendor/k8s.io/apiserver/pkg/server/options/serving.go
generated
vendored
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"path"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pborman/uuid"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
||||
certutil "k8s.io/client-go/util/cert"
|
||||
)
|
||||
|
||||
type SecureServingOptions struct {
|
||||
BindAddress net.IP
|
||||
BindPort int
|
||||
|
||||
// ServerCert is the TLS cert info for serving secure traffic
|
||||
ServerCert GeneratableKeyCert
|
||||
// SNICertKeys are named CertKeys for serving secure traffic with SNI support.
|
||||
SNICertKeys []utilflag.NamedCertKey
|
||||
}
|
||||
|
||||
type CertKey struct {
|
||||
// CertFile is a file containing a PEM-encoded certificate, and possibly the complete certificate chain
|
||||
CertFile string
|
||||
// KeyFile is a file containing a PEM-encoded private key for the certificate specified by CertFile
|
||||
KeyFile string
|
||||
}
|
||||
|
||||
type GeneratableKeyCert struct {
|
||||
CertKey CertKey
|
||||
|
||||
// CACertFile is an optional file containing the certificate chain for CertKey.CertFile
|
||||
CACertFile string
|
||||
// CertDirectory is a directory that will contain the certificates. If the cert and key aren't specifically set
|
||||
// this will be used to derive a match with the "pair-name"
|
||||
CertDirectory string
|
||||
// PairName is the name which will be used with CertDirectory to make a cert and key names
|
||||
// It becomes CertDirector/PairName.crt and CertDirector/PairName.key
|
||||
PairName string
|
||||
}
|
||||
|
||||
func NewSecureServingOptions() *SecureServingOptions {
|
||||
return &SecureServingOptions{
|
||||
BindAddress: net.ParseIP("0.0.0.0"),
|
||||
BindPort: 443,
|
||||
ServerCert: GeneratableKeyCert{
|
||||
PairName: "apiserver",
|
||||
CertDirectory: "apiserver.local.config/certificates",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) DefaultExternalAddress() (net.IP, error) {
|
||||
return utilnet.ChooseBindAddress(s.BindAddress)
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) Validate() []error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
errors := []error{}
|
||||
|
||||
if s.BindPort < 0 || s.BindPort > 65535 {
|
||||
errors = append(errors, fmt.Errorf("--secure-port %v must be between 0 and 65535, inclusive. 0 for turning off secure port.", s.BindPort))
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.IPVar(&s.BindAddress, "bind-address", s.BindAddress, ""+
|
||||
"The IP address on which to listen for the --secure-port port. The "+
|
||||
"associated interface(s) must be reachable by the rest of the cluster, and by CLI/web "+
|
||||
"clients. If blank, all interfaces will be used (0.0.0.0).")
|
||||
|
||||
fs.IntVar(&s.BindPort, "secure-port", s.BindPort, ""+
|
||||
"The port on which to serve HTTPS with authentication and authorization. If 0, "+
|
||||
"don't serve HTTPS at all.")
|
||||
|
||||
fs.StringVar(&s.ServerCert.CertDirectory, "cert-dir", s.ServerCert.CertDirectory, ""+
|
||||
"The directory where the TLS certs are located. "+
|
||||
"If --tls-cert-file and --tls-private-key-file are provided, this flag will be ignored.")
|
||||
|
||||
fs.StringVar(&s.ServerCert.CertKey.CertFile, "tls-cert-file", s.ServerCert.CertKey.CertFile, ""+
|
||||
"File containing the default x509 Certificate for HTTPS. (CA cert, if any, concatenated "+
|
||||
"after server cert). If HTTPS serving is enabled, and --tls-cert-file and "+
|
||||
"--tls-private-key-file are not provided, a self-signed certificate and key "+
|
||||
"are generated for the public address and saved to /var/run/kubernetes.")
|
||||
|
||||
fs.StringVar(&s.ServerCert.CertKey.KeyFile, "tls-private-key-file", s.ServerCert.CertKey.KeyFile,
|
||||
"File containing the default x509 private key matching --tls-cert-file.")
|
||||
|
||||
fs.StringVar(&s.ServerCert.CACertFile, "tls-ca-file", s.ServerCert.CACertFile, "If set, this "+
|
||||
"certificate authority will used for secure access from Admission "+
|
||||
"Controllers. This must be a valid PEM-encoded CA bundle. Altneratively, the certificate authority "+
|
||||
"can be appended to the certificate provided by --tls-cert-file.")
|
||||
|
||||
fs.Var(utilflag.NewNamedCertKeyArray(&s.SNICertKeys), "tls-sni-cert-key", ""+
|
||||
"A pair of x509 certificate and private key file paths, optionally suffixed with a list of "+
|
||||
"domain patterns which are fully qualified domain names, possibly with prefixed wildcard "+
|
||||
"segments. If no domain patterns are provided, the names of the certificate are "+
|
||||
"extracted. Non-wildcard matches trump over wildcard matches, explicit domain patterns "+
|
||||
"trump over extracted names. For multiple key/certificate pairs, use the "+
|
||||
"--tls-sni-cert-key multiple times. "+
|
||||
"Examples: \"example.crt,example.key\" or \"foo.crt,foo.key:*.foo.com,foo.com\".")
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) {
|
||||
fs.IPVar(&s.BindAddress, "public-address-override", s.BindAddress,
|
||||
"DEPRECATED: see --bind-address instead.")
|
||||
fs.MarkDeprecated("public-address-override", "see --bind-address instead.")
|
||||
}
|
||||
|
||||
// ApplyTo fills up serving information in the server configuration.
|
||||
func (s *SecureServingOptions) ApplyTo(c *server.Config) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if s.BindPort <= 0 {
|
||||
return nil
|
||||
}
|
||||
if err := s.applyServingInfoTo(c); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create self-signed cert+key with the fake server.LoopbackClientServerNameOverride and
|
||||
// let the server return it when the loopback client connects.
|
||||
certPem, keyPem, err := certutil.GenerateSelfSignedCertKey(server.LoopbackClientServerNameOverride, nil, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate self-signed certificate for loopback connection: %v", err)
|
||||
}
|
||||
tlsCert, err := tls.X509KeyPair(certPem, keyPem)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate self-signed certificate for loopback connection: %v", err)
|
||||
}
|
||||
|
||||
secureLoopbackClientConfig, err := c.SecureServingInfo.NewLoopbackClientConfig(uuid.NewRandom().String(), certPem)
|
||||
switch {
|
||||
// if we failed and there's no fallback loopback client config, we need to fail
|
||||
case err != nil && c.LoopbackClientConfig == nil:
|
||||
return err
|
||||
|
||||
// if we failed, but we already have a fallback loopback client config (usually insecure), allow it
|
||||
case err != nil && c.LoopbackClientConfig != nil:
|
||||
|
||||
default:
|
||||
c.LoopbackClientConfig = secureLoopbackClientConfig
|
||||
c.SecureServingInfo.SNICerts[server.LoopbackClientServerNameOverride] = &tlsCert
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) applyServingInfoTo(c *server.Config) error {
|
||||
if s.BindPort <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
secureServingInfo := &server.SecureServingInfo{
|
||||
BindAddress: net.JoinHostPort(s.BindAddress.String(), strconv.Itoa(s.BindPort)),
|
||||
}
|
||||
|
||||
serverCertFile, serverKeyFile := s.ServerCert.CertKey.CertFile, s.ServerCert.CertKey.KeyFile
|
||||
|
||||
// load main cert
|
||||
if len(serverCertFile) != 0 || len(serverKeyFile) != 0 {
|
||||
tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load server certificate: %v", err)
|
||||
}
|
||||
secureServingInfo.Cert = &tlsCert
|
||||
}
|
||||
|
||||
// optionally load CA cert
|
||||
if len(s.ServerCert.CACertFile) != 0 {
|
||||
pemData, err := ioutil.ReadFile(s.ServerCert.CACertFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read certificate authority from %q: %v", s.ServerCert.CACertFile, err)
|
||||
}
|
||||
block, pemData := pem.Decode(pemData)
|
||||
if block == nil {
|
||||
return fmt.Errorf("no certificate found in certificate authority file %q", s.ServerCert.CACertFile)
|
||||
}
|
||||
if block.Type != "CERTIFICATE" {
|
||||
return fmt.Errorf("expected CERTIFICATE block in certiticate authority file %q, found: %s", s.ServerCert.CACertFile, block.Type)
|
||||
}
|
||||
secureServingInfo.CACert = &tls.Certificate{
|
||||
Certificate: [][]byte{block.Bytes},
|
||||
}
|
||||
}
|
||||
|
||||
// load SNI certs
|
||||
namedTLSCerts := make([]server.NamedTLSCert, 0, len(s.SNICertKeys))
|
||||
for _, nck := range s.SNICertKeys {
|
||||
tlsCert, err := tls.LoadX509KeyPair(nck.CertFile, nck.KeyFile)
|
||||
namedTLSCerts = append(namedTLSCerts, server.NamedTLSCert{
|
||||
TLSCert: tlsCert,
|
||||
Names: nck.Names,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load SNI cert and key: %v", err)
|
||||
}
|
||||
}
|
||||
var err error
|
||||
secureServingInfo.SNICerts, err = server.GetNamedCertificateMap(namedTLSCerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.SecureServingInfo = secureServingInfo
|
||||
c.ReadWritePort = s.BindPort
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SecureServingOptions) MaybeDefaultWithSelfSignedCerts(publicAddress string, alternateDNS []string, alternateIPs []net.IP) error {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
keyCert := &s.ServerCert.CertKey
|
||||
if s.BindPort == 0 || len(keyCert.CertFile) != 0 || len(keyCert.KeyFile) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
keyCert.CertFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".crt")
|
||||
keyCert.KeyFile = path.Join(s.ServerCert.CertDirectory, s.ServerCert.PairName+".key")
|
||||
|
||||
canReadCertAndKey, err := certutil.CanReadCertAndKey(keyCert.CertFile, keyCert.KeyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !canReadCertAndKey {
|
||||
// add either the bind address or localhost to the valid alternates
|
||||
bindIP := s.BindAddress.String()
|
||||
if bindIP == "0.0.0.0" {
|
||||
alternateDNS = append(alternateDNS, "localhost")
|
||||
} else {
|
||||
alternateIPs = append(alternateIPs, s.BindAddress)
|
||||
}
|
||||
|
||||
if cert, key, err := certutil.GenerateSelfSignedCertKey(publicAddress, alternateIPs, alternateDNS); err != nil {
|
||||
return fmt.Errorf("unable to generate self signed cert: %v", err)
|
||||
} else {
|
||||
if err := certutil.WriteCert(keyCert.CertFile, cert); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := certutil.WriteKey(keyCert.KeyFile, key); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infof("Generated self-signed cert (%s, %s)", keyCert.CertFile, keyCert.KeyFile)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
725
vendor/k8s.io/apiserver/pkg/server/options/serving_test.go
generated
vendored
Normal file
725
vendor/k8s.io/apiserver/pkg/server/options/serving_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,725 @@
|
|||
/*
|
||||
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 options
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
cryptorand "crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
. "k8s.io/apiserver/pkg/server"
|
||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
||||
"k8s.io/client-go/discovery"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
func setUp(t *testing.T) Config {
|
||||
scheme := runtime.NewScheme()
|
||||
codecs := serializer.NewCodecFactory(scheme)
|
||||
|
||||
config := NewConfig(codecs)
|
||||
config.RequestContextMapper = genericapirequest.NewRequestContextMapper()
|
||||
|
||||
return *config
|
||||
}
|
||||
|
||||
type TestCertSpec struct {
|
||||
host string
|
||||
names, ips []string // in certificate
|
||||
}
|
||||
|
||||
type NamedTestCertSpec struct {
|
||||
TestCertSpec
|
||||
explicitNames []string // as --tls-sni-cert-key explicit names
|
||||
}
|
||||
|
||||
func TestGetNamedCertificateMap(t *testing.T) {
|
||||
tests := []struct {
|
||||
certs []NamedTestCertSpec
|
||||
explicitNames []string
|
||||
expected map[string]int // name to certs[*] index
|
||||
errorString string
|
||||
}{
|
||||
{
|
||||
// empty certs
|
||||
expected: map[string]int{},
|
||||
},
|
||||
{
|
||||
// only one cert
|
||||
certs: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]int{
|
||||
"test.com": 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
// ips are ignored
|
||||
certs: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
ips: []string{"1.2.3.4"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]int{
|
||||
"test.com": 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
// two certs with the same name
|
||||
certs: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]int{
|
||||
"test.com": 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
// two certs with different names
|
||||
certs: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test2.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test1.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]int{
|
||||
"test1.com": 1,
|
||||
"test2.com": 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
// two certs with the same name, explicit trumps
|
||||
certs: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
},
|
||||
explicitNames: []string{"test.com"},
|
||||
},
|
||||
},
|
||||
expected: map[string]int{
|
||||
"test.com": 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
// certs with partial overlap; ips are ignored
|
||||
certs: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "a",
|
||||
names: []string{"a.test.com", "test.com"},
|
||||
},
|
||||
},
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "b",
|
||||
names: []string{"b.test.com", "test.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: map[string]int{
|
||||
"a": 0, "b": 1,
|
||||
"a.test.com": 0, "b.test.com": 1,
|
||||
"test.com": 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
// wildcards
|
||||
certs: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "a",
|
||||
names: []string{"a.test.com", "test.com"},
|
||||
},
|
||||
explicitNames: []string{"*.test.com", "test.com"},
|
||||
},
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "b",
|
||||
names: []string{"b.test.com", "test.com"},
|
||||
},
|
||||
explicitNames: []string{"dev.test.com", "test.com"},
|
||||
}},
|
||||
expected: map[string]int{
|
||||
"test.com": 0,
|
||||
"*.test.com": 0,
|
||||
"dev.test.com": 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
NextTest:
|
||||
for i, test := range tests {
|
||||
var namedTLSCerts []NamedTLSCert
|
||||
bySignature := map[string]int{} // index in test.certs by cert signature
|
||||
for j, c := range test.certs {
|
||||
cert, err := createTestTLSCerts(c.TestCertSpec)
|
||||
if err != nil {
|
||||
t.Errorf("%d - failed to create cert %d: %v", i, j, err)
|
||||
continue NextTest
|
||||
}
|
||||
|
||||
namedTLSCerts = append(namedTLSCerts, NamedTLSCert{
|
||||
TLSCert: cert,
|
||||
Names: c.explicitNames,
|
||||
})
|
||||
|
||||
sig, err := certSignature(cert)
|
||||
if err != nil {
|
||||
t.Errorf("%d - failed to get signature for %d: %v", i, j, err)
|
||||
continue NextTest
|
||||
}
|
||||
bySignature[sig] = j
|
||||
}
|
||||
|
||||
certMap, err := GetNamedCertificateMap(namedTLSCerts)
|
||||
if err == nil && len(test.errorString) != 0 {
|
||||
t.Errorf("%d - expected no error, got: %v", i, err)
|
||||
} else if err != nil && err.Error() != test.errorString {
|
||||
t.Errorf("%d - expected error %q, got: %v", i, test.errorString, err)
|
||||
} else {
|
||||
got := map[string]int{}
|
||||
for name, cert := range certMap {
|
||||
x509Certs, err := x509.ParseCertificates(cert.Certificate[0])
|
||||
assert.NoError(t, err, "%d - invalid certificate for %q", i, name)
|
||||
assert.True(t, len(x509Certs) > 0, "%d - expected at least one x509 cert in tls cert for %q", i, name)
|
||||
got[name] = bySignature[x509CertSignature(x509Certs[0])]
|
||||
}
|
||||
|
||||
assert.EqualValues(t, test.expected, got, "%d - wrong certificate map", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestServerRunWithSNI(t *testing.T) {
|
||||
tests := map[string]struct {
|
||||
Cert TestCertSpec
|
||||
SNICerts []NamedTestCertSpec
|
||||
ExpectedCertIndex int
|
||||
|
||||
// passed in the client hello info, "localhost" if unset
|
||||
ServerName string
|
||||
|
||||
// optional ip or hostname to pass to NewLoopbackClientConfig
|
||||
LoopbackClientBindAddressOverride string
|
||||
ExpectLoopbackClientError bool
|
||||
}{
|
||||
"only one cert": {
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
ips: []string{"127.0.0.1"},
|
||||
},
|
||||
ExpectedCertIndex: -1,
|
||||
},
|
||||
"cert with multiple alternate names": {
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
names: []string{"test.com"},
|
||||
ips: []string{"127.0.0.1"},
|
||||
},
|
||||
ExpectedCertIndex: -1,
|
||||
ServerName: "test.com",
|
||||
},
|
||||
"one SNI and the default cert with the same name": {
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
ips: []string{"127.0.0.1"},
|
||||
},
|
||||
SNICerts: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedCertIndex: 0,
|
||||
},
|
||||
"matching SNI cert": {
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
ips: []string{"127.0.0.1"},
|
||||
},
|
||||
SNICerts: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedCertIndex: 0,
|
||||
ServerName: "test.com",
|
||||
},
|
||||
"matching IP in SNI cert and the server cert": {
|
||||
// IPs must not be passed via SNI. Hence, the ServerName in the
|
||||
// HELLO packet is empty and the server should select the non-SNI cert.
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
ips: []string{"10.0.0.1", "127.0.0.1"},
|
||||
},
|
||||
SNICerts: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
ips: []string{"10.0.0.1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedCertIndex: -1,
|
||||
ServerName: "10.0.0.1",
|
||||
},
|
||||
"wildcards": {
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
ips: []string{"127.0.0.1"},
|
||||
},
|
||||
SNICerts: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "test.com",
|
||||
names: []string{"*.test.com"},
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedCertIndex: 0,
|
||||
ServerName: "www.test.com",
|
||||
},
|
||||
|
||||
"loopback: LoopbackClientServerNameOverride not on any cert": {
|
||||
Cert: TestCertSpec{
|
||||
host: "test.com",
|
||||
},
|
||||
SNICerts: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedCertIndex: 0,
|
||||
},
|
||||
"loopback: LoopbackClientServerNameOverride on server cert": {
|
||||
Cert: TestCertSpec{
|
||||
host: server.LoopbackClientServerNameOverride,
|
||||
},
|
||||
SNICerts: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: "localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedCertIndex: 0,
|
||||
},
|
||||
"loopback: LoopbackClientServerNameOverride on SNI cert": {
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
},
|
||||
SNICerts: []NamedTestCertSpec{
|
||||
{
|
||||
TestCertSpec: TestCertSpec{
|
||||
host: server.LoopbackClientServerNameOverride,
|
||||
},
|
||||
},
|
||||
},
|
||||
ExpectedCertIndex: -1,
|
||||
},
|
||||
"loopback: bind to 0.0.0.0 => loopback uses localhost": {
|
||||
Cert: TestCertSpec{
|
||||
host: "localhost",
|
||||
},
|
||||
ExpectedCertIndex: -1,
|
||||
LoopbackClientBindAddressOverride: "0.0.0.0",
|
||||
},
|
||||
}
|
||||
|
||||
specToName := func(spec TestCertSpec) string {
|
||||
name := spec.host + "_" + strings.Join(spec.names, ",") + "_" + strings.Join(spec.ips, ",")
|
||||
return strings.Replace(name, "*", "star", -1)
|
||||
}
|
||||
|
||||
NextTest:
|
||||
for title, test := range tests {
|
||||
// create server cert
|
||||
certDir := "testdata/" + specToName(test.Cert)
|
||||
serverCertBundleFile := filepath.Join(certDir, "cert")
|
||||
serverKeyFile := filepath.Join(certDir, "key")
|
||||
err := getOrCreateTestCertFiles(serverCertBundleFile, serverKeyFile, test.Cert)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to create server cert: %v", title, err)
|
||||
continue NextTest
|
||||
}
|
||||
ca, err := caCertFromBundle(serverCertBundleFile)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to extract ca cert from server cert bundle: %v", title, err)
|
||||
continue NextTest
|
||||
}
|
||||
caCerts := []*x509.Certificate{ca}
|
||||
|
||||
// create SNI certs
|
||||
var namedCertKeys []utilflag.NamedCertKey
|
||||
serverSig, err := certFileSignature(serverCertBundleFile, serverKeyFile)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to get server cert signature: %v", title, err)
|
||||
continue NextTest
|
||||
}
|
||||
signatures := map[string]int{
|
||||
serverSig: -1,
|
||||
}
|
||||
for j, c := range test.SNICerts {
|
||||
sniDir := filepath.Join(certDir, specToName(c.TestCertSpec))
|
||||
certBundleFile := filepath.Join(sniDir, "cert")
|
||||
keyFile := filepath.Join(sniDir, "key")
|
||||
err := getOrCreateTestCertFiles(certBundleFile, keyFile, c.TestCertSpec)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to create SNI cert %d: %v", title, j, err)
|
||||
continue NextTest
|
||||
}
|
||||
|
||||
namedCertKeys = append(namedCertKeys, utilflag.NamedCertKey{
|
||||
KeyFile: keyFile,
|
||||
CertFile: certBundleFile,
|
||||
Names: c.explicitNames,
|
||||
})
|
||||
|
||||
ca, err := caCertFromBundle(certBundleFile)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to extract ca cert from SNI cert %d: %v", title, j, err)
|
||||
continue NextTest
|
||||
}
|
||||
caCerts = append(caCerts, ca)
|
||||
|
||||
// store index in namedCertKeys with the signature as the key
|
||||
sig, err := certFileSignature(certBundleFile, keyFile)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed get SNI cert %d signature: %v", title, j, err)
|
||||
continue NextTest
|
||||
}
|
||||
signatures[sig] = j
|
||||
}
|
||||
|
||||
stopCh := make(chan struct{})
|
||||
func() {
|
||||
defer close(stopCh)
|
||||
|
||||
// launch server
|
||||
config := setUp(t)
|
||||
|
||||
v := fakeVersion()
|
||||
config.Version = &v
|
||||
|
||||
config.EnableIndex = true
|
||||
secureOptions := &SecureServingOptions{
|
||||
BindAddress: net.ParseIP("127.0.0.1"),
|
||||
BindPort: 6443,
|
||||
ServerCert: GeneratableKeyCert{
|
||||
CertKey: CertKey{
|
||||
CertFile: serverCertBundleFile,
|
||||
KeyFile: serverKeyFile,
|
||||
},
|
||||
},
|
||||
SNICertKeys: namedCertKeys,
|
||||
}
|
||||
config.LoopbackClientConfig = &restclient.Config{}
|
||||
if err := secureOptions.ApplyTo(&config); err != nil {
|
||||
t.Errorf("%q - failed applying the SecureServingOptions: %v", title, err)
|
||||
return
|
||||
}
|
||||
|
||||
s, err := config.Complete(nil).New("test", server.EmptyDelegate)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed creating the server: %v", title, err)
|
||||
return
|
||||
}
|
||||
|
||||
// patch in a 0-port to enable auto port allocation
|
||||
s.SecureServingInfo.BindAddress = "127.0.0.1:0"
|
||||
|
||||
// add poststart hook to know when the server is up.
|
||||
startedCh := make(chan struct{})
|
||||
s.AddPostStartHook("test-notifier", func(context PostStartHookContext) error {
|
||||
close(startedCh)
|
||||
return nil
|
||||
})
|
||||
preparedServer := s.PrepareRun()
|
||||
go func() {
|
||||
if err := preparedServer.Run(stopCh); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// load ca certificates into a pool
|
||||
roots := x509.NewCertPool()
|
||||
for _, caCert := range caCerts {
|
||||
roots.AddCert(caCert)
|
||||
}
|
||||
|
||||
<-startedCh
|
||||
|
||||
effectiveSecurePort := fmt.Sprintf("%d", preparedServer.EffectiveSecurePort())
|
||||
// try to dial
|
||||
addr := fmt.Sprintf("localhost:%s", effectiveSecurePort)
|
||||
t.Logf("Dialing %s as %q", addr, test.ServerName)
|
||||
conn, err := tls.Dial("tcp", addr, &tls.Config{
|
||||
RootCAs: roots,
|
||||
ServerName: test.ServerName, // used for SNI in the client HELLO packet
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to connect: %v", title, err)
|
||||
return
|
||||
}
|
||||
|
||||
// check returned server certificate
|
||||
sig := x509CertSignature(conn.ConnectionState().PeerCertificates[0])
|
||||
gotCertIndex, found := signatures[sig]
|
||||
if !found {
|
||||
t.Errorf("%q - unknown signature returned from server: %s", title, sig)
|
||||
}
|
||||
if gotCertIndex != test.ExpectedCertIndex {
|
||||
t.Errorf("%q - expected cert index %d, got cert index %d", title, test.ExpectedCertIndex, gotCertIndex)
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
|
||||
// check that the loopback client can connect
|
||||
host := "127.0.0.1"
|
||||
if len(test.LoopbackClientBindAddressOverride) != 0 {
|
||||
host = test.LoopbackClientBindAddressOverride
|
||||
}
|
||||
s.LoopbackClientConfig.Host = net.JoinHostPort(host, effectiveSecurePort)
|
||||
if test.ExpectLoopbackClientError {
|
||||
if err == nil {
|
||||
t.Errorf("%q - expected error creating loopback client config", title)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed creating loopback client config: %v", title, err)
|
||||
return
|
||||
}
|
||||
client, err := discovery.NewDiscoveryClientForConfig(s.LoopbackClientConfig)
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to create loopback client: %v", title, err)
|
||||
return
|
||||
}
|
||||
got, err := client.ServerVersion()
|
||||
if err != nil {
|
||||
t.Errorf("%q - failed to connect with loopback client: %v", title, err)
|
||||
return
|
||||
}
|
||||
if expected := &v; !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("%q - loopback client didn't get correct version info: expected=%v got=%v", title, expected, got)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
||||
|
||||
func parseIPList(ips []string) []net.IP {
|
||||
var netIPs []net.IP
|
||||
for _, ip := range ips {
|
||||
netIPs = append(netIPs, net.ParseIP(ip))
|
||||
}
|
||||
return netIPs
|
||||
}
|
||||
|
||||
func createTestTLSCerts(spec TestCertSpec) (tlsCert tls.Certificate, err error) {
|
||||
certPem, keyPem, err := generateSelfSignedCertKey(spec.host, parseIPList(spec.ips), spec.names)
|
||||
if err != nil {
|
||||
return tlsCert, err
|
||||
}
|
||||
|
||||
tlsCert, err = tls.X509KeyPair(certPem, keyPem)
|
||||
return tlsCert, err
|
||||
}
|
||||
|
||||
func getOrCreateTestCertFiles(certFileName, keyFileName string, spec TestCertSpec) (err error) {
|
||||
if _, err := os.Stat(certFileName); err == nil {
|
||||
if _, err := os.Stat(keyFileName); err == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
certPem, keyPem, err := generateSelfSignedCertKey(spec.host, parseIPList(spec.ips), spec.names)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
os.MkdirAll(filepath.Dir(certFileName), os.FileMode(0755))
|
||||
err = ioutil.WriteFile(certFileName, certPem, os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
os.MkdirAll(filepath.Dir(keyFileName), os.FileMode(0755))
|
||||
err = ioutil.WriteFile(keyFileName, keyPem, os.FileMode(0755))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func caCertFromBundle(bundlePath string) (*x509.Certificate, error) {
|
||||
pemData, err := ioutil.ReadFile(bundlePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// fetch last block
|
||||
var block *pem.Block
|
||||
for {
|
||||
var nextBlock *pem.Block
|
||||
nextBlock, pemData = pem.Decode(pemData)
|
||||
if nextBlock == nil {
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("no certificate found in %q", bundlePath)
|
||||
|
||||
}
|
||||
return x509.ParseCertificate(block.Bytes)
|
||||
}
|
||||
block = nextBlock
|
||||
}
|
||||
}
|
||||
|
||||
func x509CertSignature(cert *x509.Certificate) string {
|
||||
return base64.StdEncoding.EncodeToString(cert.Signature)
|
||||
}
|
||||
|
||||
func certFileSignature(certFile, keyFile string) (string, error) {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return certSignature(cert)
|
||||
}
|
||||
|
||||
func certSignature(cert tls.Certificate) (string, error) {
|
||||
x509Certs, err := x509.ParseCertificates(cert.Certificate[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return x509CertSignature(x509Certs[0]), nil
|
||||
}
|
||||
|
||||
func fakeVersion() version.Info {
|
||||
return version.Info{
|
||||
Major: "42",
|
||||
Minor: "42",
|
||||
GitVersion: "42",
|
||||
GitCommit: "34973274ccef6ab4dfaaf86599792fa9c3fe4689",
|
||||
GitTreeState: "Dirty",
|
||||
}
|
||||
}
|
||||
|
||||
// generateSelfSignedCertKey creates a self-signed certificate and key for the given host.
|
||||
// Host may be an IP or a DNS name
|
||||
// You may also specify additional subject alt names (either ip or dns names) for the certificate
|
||||
func generateSelfSignedCertKey(host string, alternateIPs []net.IP, alternateDNS []string) ([]byte, []byte, error) {
|
||||
priv, err := rsa.GenerateKey(cryptorand.Reader, 2048)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
template := x509.Certificate{
|
||||
SerialNumber: big.NewInt(1),
|
||||
Subject: pkix.Name{
|
||||
CommonName: fmt.Sprintf("%s@%d", host, time.Now().Unix()),
|
||||
},
|
||||
NotBefore: time.Unix(0, 0),
|
||||
NotAfter: time.Now().Add(time.Hour * 24 * 365 * 100),
|
||||
|
||||
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
BasicConstraintsValid: true,
|
||||
IsCA: true,
|
||||
}
|
||||
|
||||
if ip := net.ParseIP(host); ip != nil {
|
||||
template.IPAddresses = append(template.IPAddresses, ip)
|
||||
} else {
|
||||
template.DNSNames = append(template.DNSNames, host)
|
||||
}
|
||||
|
||||
template.IPAddresses = append(template.IPAddresses, alternateIPs...)
|
||||
template.DNSNames = append(template.DNSNames, alternateDNS...)
|
||||
|
||||
derBytes, err := x509.CreateCertificate(cryptorand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Generate cert
|
||||
certBuffer := bytes.Buffer{}
|
||||
if err := pem.Encode(&certBuffer, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Generate key
|
||||
keyBuffer := bytes.Buffer{}
|
||||
if err := pem.Encode(&keyBuffer, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return certBuffer.Bytes(), keyBuffer.Bytes(), nil
|
||||
}
|
||||
20
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/cert
generated
vendored
Executable file
20
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAiGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMS0wKwYDVQQDDCRhcGlz
|
||||
ZXJ2ZXItbG9vcGJhY2stY2xpZW50QDE0OTYzMzA2NTYwIBcNNzAwMTAxMDAwMDAw
|
||||
WhgPMjExNzA1MDgxNTI0MTZaMC8xLTArBgNVBAMMJGFwaXNlcnZlci1sb29wYmFj
|
||||
ay1jbGllbnRAMTQ5NjMzMDY1NjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAMScrdc+XlUPHsDvnN2fFxSSQrhrKBlrHBcelaiFwR1vWt2EXxErVUoRGA+1
|
||||
jvQ5Y4NePS46Zpq2N+yibiWjaC0fsS4W2UvPAfdD4nxOp5SaTBy5A0XI+JU8QmbI
|
||||
Rls+C/fSeYaXSKp5Jq41uTKGc+QwT9pqCeV7N74DSc6kvNh7mIBc4YCOzMnhQq5H
|
||||
KQVCeDPURxfh7Ew+v13dLYDq7V6O1jBjfg3Rm/RRFnM78kcc51QTOo9ZSs5HgahU
|
||||
eM4YirHz8ujswhgxzH43TeOGds3McGKv1HMHKJKinX02IHJBqV6U5y/AH92wi0Sp
|
||||
IKgJ4GJaj/Vda/kPUtf5cL2sOt0CAwEAAaNeMFwwDgYDVR0PAQH/BAQDAgKkMBMG
|
||||
A1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wJAYDVR0RBB0wG4IZ
|
||||
YXBpc2VydmVyLWxvb3BiYWNrLWNsaWVudDANBgkqhkiG9w0BAQsFAAOCAQEALlZe
|
||||
LJ3i6WQhpgV3jPWuIBTXJ0bb3cbYnk4JLWYW9sIjMakfAlZK9VrfRZKOhI8DsGsG
|
||||
PVcYOyc5TQM0JkcsCKajyA6HktpMFEtxBfSJOlRAdvvj1GtRnifcA5xBqn1SzL2H
|
||||
tFx5etii70spZD3jDht2bKLZmL2NbGGVOWiKdtz6qR+V9U4F+uYCq+phKgnLeDz3
|
||||
RiMIGvDpliWU/R1jgdi3RENtP8QGhjiwVDGYJBB26ZQGDkd4HYPZj9bROMknnRFk
|
||||
fbP/P+cmvXrwScvrXLFkNQe/4LfJAJnod4ropQ84z/Cu2JaFbJau4TSlbp57y9ON
|
||||
7fpK9SV4bTCyO34Uew==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpgIBAAKCAQEAxJyt1z5eVQ8ewO+c3Z8XFJJCuGsoGWscFx6VqIXBHW9a3YRf
|
||||
EStVShEYD7WO9Dljg149LjpmmrY37KJuJaNoLR+xLhbZS88B90PifE6nlJpMHLkD
|
||||
Rcj4lTxCZshGWz4L99J5hpdIqnkmrjW5MoZz5DBP2moJ5Xs3vgNJzqS82HuYgFzh
|
||||
gI7MyeFCrkcpBUJ4M9RHF+HsTD6/Xd0tgOrtXo7WMGN+DdGb9FEWczvyRxznVBM6
|
||||
j1lKzkeBqFR4zhiKsfPy6OzCGDHMfjdN44Z2zcxwYq/UcwcokqKdfTYgckGpXpTn
|
||||
L8Af3bCLRKkgqAngYlqP9V1r+Q9S1/lwvaw63QIDAQABAoIBAQCCiJ9mM3M/t80s
|
||||
PSmBflk9gCmp9QLndVRuCRFFlJ/1IfRK89KGPKXgid7WtyzU+rEWC+BEq+NpPid9
|
||||
BQ5qEMZehxcESYk1KpfciYZ6D2J1Z8e2niMNFvPraNmREdCcTWJ2w5eJgzFsdQj4
|
||||
SpTNdbTFwEmT8ijl4dvNXLOI7ZivhJ1vVGW2qUJQyR5xo+GuXZZVVJW6vndkzHoA
|
||||
SJDSii4PMAisibu+Uw8JQM1rYFD2olvSWSnjz+kCk2IbrqhVPOW9PoYETn2DxXfY
|
||||
0miUGq4KwtNjFnqJO+CDTJL6Y7LrZbKdSVTKfiY5ReLckWhx4RKn01T9RhsrS979
|
||||
bee+iynlAoGBAOVDKy9t3kN7FTQJyMGa3QAVUV8B5OPUIyuUOhzMrbIFECtYFXxY
|
||||
jsnRNf1i2PbVsl9WDrPikNCVyn5Tf4xU7YUH7/Jo+Ha978Fu9tNf95RK+A2GKtiO
|
||||
8jQa6nkII8Fmql9o3xjCEQD0VBirrm1h3sbC3ylKL4u+yC0a6W7vFRRfAoGBANuK
|
||||
tIvCRGoEyn8nzSXUez7+/9sIwU7jV6lXL7qgdukr7o/ZBqer48CDlehXU48yiVpB
|
||||
TuMZnloj/Ndf9X07t/yYwfCP7lpdq6lR7EhG5yS0WjgHjv5+QKLVA7ocrEufgBKA
|
||||
Wdb+96eyOb1bgBMprtYA8wbhblhut8rk+i8SkdpDAoGBAJPxY0XI/VvIamdZRP6C
|
||||
Y8iw+QB5JW5Lr6JolZOOFSxCjiuaPMswDnx3kAKCLgYi4mcZDoXQV6jycGhSk4VI
|
||||
LLni5FkmmeduK/BEHe1LCWXddvKrOKzkmbE8CUCvGvurt5e4AfdlDDn0cOw2pWma
|
||||
dXsHt6BaahIgu+Y3bpmSmKS7AoGBAJGlhX2qleSX5RdSemD8g/8ZAPh46HXDkSaA
|
||||
9uMjDYBHau73ldiqyl+vbEZFmOPJLSI5bXJwToOSmGzx9lTsx3TGivNjKUe+GOIt
|
||||
vUl98F6DX88IBK+FZrAgDWbtGsjYugiMKg3d/MK+FFwxVdhgsOK8on4QFDqKyVfL
|
||||
bMbJ1QUtAoGBAK4nM5GZyefXylAnq+pzGTStu0Q1AVzN0iWO0G0Q6gaItvk82Scf
|
||||
wVoEGE9a2pDCR1I1mbJn1jRxd6/GtXvR4TJSxPILS+B8aZFkxl9WIWjv/mnKJkVL
|
||||
PmSNPbieuban9TRBu6nQfWOfLMHmBnG/QXbx2X1WCRoXjICT41gJ5et3
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/localhost__/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/localhost__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTQ5NjMzMDY1NzAgFw03MDAxMDEwMDAwMDBaGA8yMTE3MDUwODE1MjQx
|
||||
N1owHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE0OTYzMzA2NTcwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDhpeY6j6ujZc2W0paC9pmVHlEhbnsH/L0N3JjA
|
||||
WooyZWgsARSlv95Vh2M1gVymu7mah3HHzQkVe6vN5bfngOO4A2d3fxYP0304+3YE
|
||||
TCariZzcEk9Ot90VHNasTVMOqZ2qdGRUvEXbpRWlIhA+T9FgNxpBDQV5yb5w67Dl
|
||||
kBM22wn5l/YM/4HqSOTVQON1SmmLUPlnWtzxVRDKPzI2zxpKkzW4U1bYgdwM/Xeo
|
||||
B2GjU+1UsBlJWGrRQhCkiFuZ92agls1bsu3+PE2leoQf6vZE+pc0m89v0lqlcgOQ
|
||||
TcI19BFvLjUneDbu4kFZWDO+f23hhEcHut26IXtOoAIvk9uhAgMBAAGjTjBMMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAHlVy
|
||||
2H1FxDXAJKeZkFCNAHOiThcm79s7P8+BXnYbQMgdHAiQIQFQDDwbntA607m0FRoZ
|
||||
1H1n/rzkRXRrNJRPEd3aSnurq6oMMZa1Pai4e2f+kyThpIw6FtI2y/Jb7lKB428g
|
||||
9AHtKSd71MEArOvWHi1IcX6rmH/vU5ezBgPpjtEC/5bI8PJMqr2osesjppKwwwjf
|
||||
BTq+AjvxltPDb+fykto41IMzpM7KRO/vat/dZ5OZZOhDpbJlAAWUfCdlWcL0kqTt
|
||||
ok+biBB4xShOwpSyKCmJg4otlvN7qJ34fCjYkQY+PXzGS6YV34Wj64F8mReTt4LO
|
||||
glampIa1cLUWPMlYPQ==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/localhost__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/apiserver-loopback-client__/localhost__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA4aXmOo+ro2XNltKWgvaZlR5RIW57B/y9DdyYwFqKMmVoLAEU
|
||||
pb/eVYdjNYFcpru5modxx80JFXurzeW354DjuANnd38WD9N9OPt2BEwmq4mc3BJP
|
||||
TrfdFRzWrE1TDqmdqnRkVLxF26UVpSIQPk/RYDcaQQ0Fecm+cOuw5ZATNtsJ+Zf2
|
||||
DP+B6kjk1UDjdUppi1D5Z1rc8VUQyj8yNs8aSpM1uFNW2IHcDP13qAdho1PtVLAZ
|
||||
SVhq0UIQpIhbmfdmoJbNW7Lt/jxNpXqEH+r2RPqXNJvPb9JapXIDkE3CNfQRby41
|
||||
J3g27uJBWVgzvn9t4YRHB7rduiF7TqACL5PboQIDAQABAoIBABg+G6MVBmqyqEjY
|
||||
7SS627tCm5LM7Njp1WHk3mBkGc7aSDvaDd7U400rE4SPFmYvavxoKtg9dglh1Q50
|
||||
7LIpjc2aSAZ1l+gxX5W8pmxFxi3NxhV6vhyF8nGvPH0cfZzoqjDC+niEFblIXi/0
|
||||
newj5SnCIcb1YC46d4RoPgQb24ScBiQi3DeuqiDPQO3cyePyr5TEpeL4djB8YgK3
|
||||
yAgKFotsox1sMz1r0AX4JdoC2cYBh6qmGs+qdkMYW3O5XKjkkWS7dglqHIchhsrr
|
||||
P38kuyp30fMrE40+5G9hKi3WO/nl3FP/eDM8BD60QF1edXobszfd8BsVnrBfbfjP
|
||||
wDP51rECgYEA5/hLWVPXvTkSJeWzkEYx2PvubClr4fZE8j4oYTikD3q86R6UkEhe
|
||||
c/yZd9WNBgwUrLG4SV3TaqEWvyUEnsaNbI1YK7e4PrhMSLLZ0G040jW8TA1SE2kT
|
||||
fnJ5g4ZRWTNvksBTcs3QtMC8FzFazQ7gKIYz+SXopxeww5D8T/ok/WUCgYEA+QXz
|
||||
/mYUnUMt4kWfcMDDbiYCgVSJK71E5M5qH2yxjB7NFLcl0+udQ5FrrW2YEgr3DnSV
|
||||
Okbg4ZcmdnjCOU7Xu5BlU0GWe5lXiOBf0sUxrNU3OY5voVPYgSXJgTPRzguTpetA
|
||||
2Liv/7x5dZ0krIKRTCKWu0iwcdOOaPrEouln740CgYBcrrckXLyBLTFUFP/2sCUw
|
||||
hhBQBQjGwsfvZehqV+AtEyQxnHdNYqzieNW0rI1pEiCck3GHVsam/vAAtUCwU7Kj
|
||||
4aGN7hzeanKIRXWDsnAWhUPTuyrE+KHCTujAJUWRd1yn7NIqt5Mg9TTxq1WVQtFL
|
||||
P7u6W2wmfu+wY9h79/0ZhQKBgQCQoRt/wmvwlmbcxxD+HIakv6Cu2pk8dRXBSf0q
|
||||
uSc5OV5kzKZEudBBwQDn3gH3YLTyiVVuXmIXw4DMSACN5jO3tPqUa0BqErpbPe2J
|
||||
bYwXVFJZtFczPI4/G+I3jxAoemLLd6HW/RUDE8T7QUReiQq4TFJ8/Y+03GkWoaZ4
|
||||
89SwtQKBgQCmWHxz0eXQk3YRk0kupSrK88pZoc9wvcWYhzkiQpP6+hYPxH+n+U6s
|
||||
NjHnQAVlRUIP0VisQRkBC4iy2ca/gVVhKgygVBuHC97fKAHlpUlO6Y7Ow031stAy
|
||||
K3zrTjuVwsz8C6bFCxHl5ojP+0cSoK7qej09vj/te1DNvkYuE/0j4Q==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
20
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/apiserver-loopback-client__/cert
generated
vendored
Executable file
20
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/apiserver-loopback-client__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDOTCCAiGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAvMS0wKwYDVQQDDCRhcGlz
|
||||
ZXJ2ZXItbG9vcGJhY2stY2xpZW50QDE0OTYzMzA2NTgwIBcNNzAwMTAxMDAwMDAw
|
||||
WhgPMjExNzA1MDgxNTI0MThaMC8xLTArBgNVBAMMJGFwaXNlcnZlci1sb29wYmFj
|
||||
ay1jbGllbnRAMTQ5NjMzMDY1ODCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
||||
ggEBAPGTpeR8wkr9yxMV8LLI7UaImRF6T0ZT2HL7cmjZDbV3Ow9L8UXvlQ0SdKbT
|
||||
ph2IU5Ww4h3xSiXXXGh0FSXALs1Ieg8DmMYKHNcnOXpShwuXBJ0NCpPSLnAO+2hg
|
||||
Jq+ULvBTGjQhvCX+cjI5IquYp0sSbERS/+46cGrwWoye67Z9Xo6enNjHG8YZf35B
|
||||
Wspvm0awUw1KcXkp+lJ9zoblBeXm+uXdMhzEyRpghRslwvtPKyU8YhTN63p0Ru1E
|
||||
Kig3pctotL2n1/7+tR8yhjEstLiydHYWhLkX6wsUv+rNirUGVUBjF5VFtERzMPRK
|
||||
0dh/hs3v2JSby56IxRcqqTSzvfECAwEAAaNeMFwwDgYDVR0PAQH/BAQDAgKkMBMG
|
||||
A1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wJAYDVR0RBB0wG4IZ
|
||||
YXBpc2VydmVyLWxvb3BiYWNrLWNsaWVudDANBgkqhkiG9w0BAQsFAAOCAQEAEEd0
|
||||
MOqC0POVtxGCy/BHR6sVcf1r1BVTt28p7u1yJIdrOKuknlILt9iSNfFcNPRWIP+u
|
||||
7SNPT8td6yViBgn7UMjvFxZgQn6kTsgTfdRV7eMxoX/jYzt0g44k0oVGv+xiJ+8q
|
||||
m5Ng+tMOMuY1+JganxNad3mwdff4uKxXuVQsV+mjXBwaJHIdxBmCFqg/dGB60lyb
|
||||
fxFjuTAT8xyL+UE3VUoCLFdNDgIsm0v3uj8nBsASW1LeZSbMQKUJXGlvEpj5U89d
|
||||
B9ZouygfISPKQ02WYYDfSmtGCqX5sFZRh2uSPp0/UPUB+hUk7C0KN6TgsB9kOAD1
|
||||
Sa0X3+KhMRVOk7riaQ==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/apiserver-loopback-client__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/apiserver-loopback-client__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA8ZOl5HzCSv3LExXwssjtRoiZEXpPRlPYcvtyaNkNtXc7D0vx
|
||||
Re+VDRJ0ptOmHYhTlbDiHfFKJddcaHQVJcAuzUh6DwOYxgoc1yc5elKHC5cEnQ0K
|
||||
k9IucA77aGAmr5Qu8FMaNCG8Jf5yMjkiq5inSxJsRFL/7jpwavBajJ7rtn1ejp6c
|
||||
2Mcbxhl/fkFaym+bRrBTDUpxeSn6Un3OhuUF5eb65d0yHMTJGmCFGyXC+08rJTxi
|
||||
FM3renRG7UQqKDely2i0vafX/v61HzKGMSy0uLJ0dhaEuRfrCxS/6s2KtQZVQGMX
|
||||
lUW0RHMw9ErR2H+Gze/YlJvLnojFFyqpNLO98QIDAQABAoIBAQCu+8LrSU83ILSr
|
||||
I3ZRGmd8cs9lMJIhn/Ql/Wq29ZoPesjl4J1oig0OBfNHMAGgXLImuJRhGfkahe+i
|
||||
nYr/6EA+ZCQ9od5UfNi9I+VnbEXzqDUoSV0DC/ilJMUIqrT68zw4ygkt/82YO8Ge
|
||||
DXLGlvF+2F5h8zj854msfGMqfqbAbJDynA0ZDuq8SORUb/1RIiAZRrABZ1n6HsXv
|
||||
cZb12wcycbxKTPfISkJDJ+PUVmCcm/eO3jLDgy2hAY5bCG6+HIO/N407Ze83TA81
|
||||
nBRM2WEA9BJbRmmB2yAEy2aJamT5lo5qORElmW0gEpAEceRgYi/VjPyvwjOw7IW3
|
||||
SlEDaYbNAoGBAPzt80mwWzgLH0SIqUadXbJILqZ1j/0sCr4r9CfIPppbZckaXbaC
|
||||
iokh3dRaVBndI8z/aPz5+wmvNjqxQ4Jz+411ILK7G0ogYRJcTaQB7Vc2QTPtdu3t
|
||||
HE1xCktyTAcyVJtrVYfpqaAKZ/eEnDmatxrMW1nmnFlZxBZErnSAjMl7AoGBAPSC
|
||||
anORXVXqbM6d7XKXiXYLkagmB92LP36DQ3OXiVIH3CD2weItRvqup9+nIdMJMDyN
|
||||
PR9CXSQXclFjVqpgPFKEAUNcGbTea45Kykw07CUYEdYRXRYtij0hhsRon+T6vsE7
|
||||
eEQQySG0ub6cTsFi/AuxWlPCkQLIhfCkVW4kvqyDAoGBAKBXEGXkgM9Ubav6nqT/
|
||||
WciNbdVkrbUnWGAaaRtXHCiqTCpiponu03f/mirmYG5M/EcWmy3UI0qOYmhuu4dV
|
||||
k2CnoeTsLG6ffCoxjZ6tAhLJeCg9H6nRKKIHSSlu+beONoiYUI5qmHe2YRZOYOM7
|
||||
q1w3tKmXQgJ6n44mWBnO7HFFAoGBAJF5Rnyb8Dw9zZ7gt3WGU3K4T2ELv4JAZvaO
|
||||
goMzlbAInR+/sHsxFRnw8FBQz/8PaHOeVM9UXLYsfQePYvDbcQhnIZlCIslJzBRM
|
||||
yy+sDRPEzlfe3NNlJOpaGCSjWv6RcQzmpRdaJVC4hgqyVOkaS3tpeaR8Dmrpg0c5
|
||||
ftqZYhihAoGAMRbt+21F1BD3AGDseGIg4MQIEPfk7nh/cVFBcnXAqV49dmm1/g7G
|
||||
L2itzJlTb1oOnlY9qBksocvnj3w8hsrWFvt/s0wv1e3uUJ1elCb18Rm4UQtr9td6
|
||||
XFqEgDaDYyj1iEmn+PrjQc+DqHn6JySLW4LTM82KWwDnMttvwLFhaOM=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTQ5NjMzMDY1NjAgFw03MDAxMDEwMDAwMDBaGA8yMTE3MDUwODE1MjQx
|
||||
NlowHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE0OTYzMzA2NTYwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDozgb3GKC3n9wREqYUm92/T0+NnEYkaWOitXAH
|
||||
BW/Gj9PAF4sggnAey62H7mkBaUpw3s3RXyrqnRqrXVWvWgbSpf0Elp3zfos4raEf
|
||||
hgWhqMh/SEf5ETHNRyfebxTSeQYRuZsvAPEsRWKf3AKFEykfW9y/GnKYM1elL0nx
|
||||
Fgh0tq943epjHuzKfVWp8LxnFd7fyL+BQsvBNnYz0SH0F9oTUU3nDCS6Os/+EtNQ
|
||||
SjP54HQ78XczI9AMvsFUS7yFEQdM8VW9uYhdv/Oyi9SLywhOCHSnV9buJ6F8URzO
|
||||
B5Cawdteew7U+EAXbEwY2D84NARF/J8iA3Eh7+UKd27WA+pJAgMBAAGjTjBMMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEA5vjR
|
||||
Q4Rjx8ko8+Zt9gRceKCVWEmdZLGkjwdW04YaIcrh41svm+tgxwUMdQ2beNjUbLZX
|
||||
DAVvK99HiANBc9Ee4DyQFkLI2z0EiAJpuxMjZlyzMmKLeijOkIkxYIshhfc2Y2Fr
|
||||
NIf6OXmFBEjqsudxkQiGfSqc443GseJmmiuhW3JR1a6ARgut4AtPsPv2TijX7tcR
|
||||
Z9tjzAMiubNDQq0g/Eh9vLZkv8tNTqoJ5N5XV8jcovNWRcIIzl+lXBfy0o68LwO4
|
||||
Hn/MzcMxOdbNfFw5nMaDuQK4oO0vT3D9nedQkdV3keLZtagN9/PO1XtkCSQ5q7Le
|
||||
LS6fs5zA/9sR2kEuGw==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA6M4G9xigt5/cERKmFJvdv09PjZxGJGljorVwBwVvxo/TwBeL
|
||||
IIJwHsuth+5pAWlKcN7N0V8q6p0aq11Vr1oG0qX9BJad836LOK2hH4YFoajIf0hH
|
||||
+RExzUcn3m8U0nkGEbmbLwDxLEVin9wChRMpH1vcvxpymDNXpS9J8RYIdLaveN3q
|
||||
Yx7syn1VqfC8ZxXe38i/gULLwTZ2M9Eh9BfaE1FN5wwkujrP/hLTUEoz+eB0O/F3
|
||||
MyPQDL7BVEu8hREHTPFVvbmIXb/zsovUi8sITgh0p1fW7iehfFEczgeQmsHbXnsO
|
||||
1PhAF2xMGNg/ODQERfyfIgNxIe/lCndu1gPqSQIDAQABAoIBAQDBke1Ar32SsJzu
|
||||
pS7qm975HxzqhjHcPLTXqT0I2YBi/Vv3sJvB4DMRqBEVoSo2PjHjP56qpBKvGQn/
|
||||
yo9gSd66b8G9eu7Cc0mXSudZqror/IcZLiSJ974NDUkdLgeGKZLeUhGyN3pQcTDb
|
||||
8rc1pwTeSid1zqvR2X7ankNA7i0BvP6UXlvWM8kUvfFDwPXhHPZtkFELc4TeGIzB
|
||||
e/l9Uk0DAgplkhM32s+0rTZ7wmcSUK9eh7/kiQCUKvdCVwegsK9iCVMaYIMggut1
|
||||
3KEHuVIbuLdjFu/tF4SN85funMIPxD7rKf38aDrR1vhQc5tq88dKwVrD8EANcA/f
|
||||
34mAWSIBAoGBAPZLAGJ2McxIoK7ymyvGdNta0glzzbww20xLB3MHYpo+cgff4DN0
|
||||
3nA/es49So1m/r0MKjnC0qxVMceTrU4u43nROwbLNlFBZ8hzoEVobCCoykWKkhQO
|
||||
hS6PkxBZ2s8nsSQCr3X+CpHd+JqT7Y98ydWVnE+kDOFIPr16j1SbBJS5AoGBAPH6
|
||||
73BDmHk4fBb9J2edNtx+7/1/+Lbb96qyy7LnA9zxdXgqjeZwFW/tNjtD2l+TPWlR
|
||||
O1L0NkfnhzWPd7rDlQA7Tzr0TLF/H34+ycbaorc22M6GEg9zJwRY42EGbYJE5uRZ
|
||||
Sq4jXOTOAta/lb+UjqWI7eYyH89SKlYcDut1VVoRAoGBANucUvJ427yBAi4dC3nE
|
||||
EszJNcHR7aAhnFlyCWlrK2PHzJvy5xJa4kEKiXE+u6xMK1bccwZgDsQ1jxabOowd
|
||||
SR6FD3HmXz+i52yYlU9uZgPVHc9WWRzjOaQjx4lr5ayTguNONbArXrdkStlikBBC
|
||||
wCHfrqpg6E80wSTpFMbnFvWZAoGAPNSDOqqTeu27LCBJtp9r+jQi6owwHu/fK9c0
|
||||
o+21J+a/TA0OjP5iNTfhqTZegCZIiuAz1J+1wY+visRdKeMe4ucF/OELIpe9eAQa
|
||||
v1WZEAvGpuxHQAEHru0Vg/+UqeneTUsxHeOf55juV8Pjxjx3O5/XhkXVf029fNYi
|
||||
3ggqA7ECgYEAgowePEGL7l27raa6F9h4wuosfuFFdrrcOJeGxNV9jAe2s2KKoQFO
|
||||
lCv/AcCsRepf9G235TqQ+if8a4p7OsHL9kxOspKBJKnwq3u+jgcJTl+HLWgnDjIc
|
||||
Yk2xlnRC4N4OiGQrxjOmFXQdXiBK3T4CJnw5g0MynrdwiJCCCnEIG+A=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDFTCCAf2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTQ5NjMzMDY1OTAgFw03MDAxMDEwMDAwMDBaGA8yMTE3MDUwODE1MjQx
|
||||
OVowHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE0OTYzMzA2NTkwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQD2NQ2T3x1nBhqWOCjiED71R+j80XsFxSMZXNJB
|
||||
wUCCGZysf6OGO6M+NYZ2qov58gfGrcZYBJWwuSRF+0NeoVzkCu3AJkTKXSdtv2vi
|
||||
M248yBUhvwPwbTcoMyG7p6LhLlmlPpP/7n9yVfVtRzFgy4UPYAlJmTnakk9jDKXM
|
||||
gi717BjntQ01+2I/HIZls5B5vLwWuHtWrdng39Evn1RwtJrLUVJE5b6/+otvokXB
|
||||
DpKMb/aI230uw9D3Csi+6iAKK9JuhCEe/S3ezJWg5SugG6UrwGBLZYavXP7iiB9p
|
||||
u32XIe8lMqQfsiur7iCQehwxTdMJ7uR5STS5eqfoV2SdnlN/AgMBAAGjWjBYMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MCAGA1UdEQQZMBeCCWxvY2FsaG9zdIcECgAAAYcEfwAAATANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAuVVTMV237szi8c9Oy5ulsmgYkp6MqfkKryy6Zi6r5UtJAWToAFu3
|
||||
dvHKnSQNgiWDewVKn+pMGNZuo3o6PGxdq/E2ZQ6/oCxNRThb7hC5IK4Yot40EUQ4
|
||||
ZavrmON0velXZJ3MwbzSzstLnJCR3maO+Ko0KPC/u4LADCQyeVrEZ4IP/RPQ2KZt
|
||||
mdmeHTm53tLPrcNSnaoW+tMs6JxAT30xOLnjwYfEtbMCVhGB+aHQIGFS620qZ+dn
|
||||
oTqrjempIIos0Vu68ZhCaDwgB9FiNtFMplxrBvOIfWO1Dz9AwPxP1fCMlZwtFJt8
|
||||
vuRu05WPJelfnEKXIuGhuGf369xyfw5cIA==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpgIBAAKCAQEA9jUNk98dZwYaljgo4hA+9Ufo/NF7BcUjGVzSQcFAghmcrH+j
|
||||
hjujPjWGdqqL+fIHxq3GWASVsLkkRftDXqFc5ArtwCZEyl0nbb9r4jNuPMgVIb8D
|
||||
8G03KDMhu6ei4S5ZpT6T/+5/clX1bUcxYMuFD2AJSZk52pJPYwylzIIu9ewY57UN
|
||||
NftiPxyGZbOQeby8Frh7Vq3Z4N/RL59UcLSay1FSROW+v/qLb6JFwQ6SjG/2iNt9
|
||||
LsPQ9wrIvuogCivSboQhHv0t3syVoOUroBulK8BgS2WGr1z+4ogfabt9lyHvJTKk
|
||||
H7Irq+4gkHocMU3TCe7keUk0uXqn6FdknZ5TfwIDAQABAoIBAQDKBSg58HZAxcpk
|
||||
/nnVXL08KCAt6nlGd8bgFcDUgYy78+YRyL3V8/NzpRnLHw+yLH1i8c3xBPoKwy/X
|
||||
WF+kM0V0PN9urjf90Ef9DneoaMkIiKiGQyUpT4RVsPb58oh8urqGE33/H6RlndC/
|
||||
NQ54PSbWQdf9EwRcq3cGcCdPUnbUA6Ur/m03kIWYMnPKZeXqTCA8AMfsLuoXyf3G
|
||||
H9Q/ReGWok2fOkw8I/2D2HOXh+vnPoOOWbxf75DAt7tk2tRrgvQ7cI6TOJo6AImD
|
||||
E1rHU7lrVhyo2AK+8BfjihGgtgt1qCwEeviuJn20wRQwTpi3r+aflaVCgsZADoc5
|
||||
6bEkjP/pAoGBAPZVyIIg4W44lHz7xu0DMAbdK2CwaYPPhEewVNTCmtq6wvP/CKz/
|
||||
gZRv7HLFrEAPuMuSPi0+0lzbUf4IfHeP79jzPEsoHlzaSyKVho91uttsTYGRS0yD
|
||||
M2Kd7uavXH/lRR16h6TlGRgNTAfWh8I+WimzBi1IHP8LQPv9hS5CUseLAoGBAP/d
|
||||
/FLXTQJ4bsb3Yune/xoerrMo05cUY8BM6WZ3s2wdPSMnTkh921CyzGzfWLpDHrww
|
||||
rFBrbs0O7YijRU5gjwddxVMT/qcwNEETEWw38X3kfPx8TCYKT5OsD3HoiTKMUVJG
|
||||
ehdADEm8mh918pgSvW/hvFaW98rIWjVwTt9djkJdAoGBAOkQJJMp9N/J3IOb24wn
|
||||
desomn/yqQIy9p61MK3UKZtFFGqyI006uSjt9DJq4BwEQ/0nMniwdWuxfhPxh4G/
|
||||
x1fmWmdpX8DOMERaYGOfKdms0HAEPc/4Wke10XCk/clr7EaFxYy3zVQ6UHMiAHRB
|
||||
diD55qA1GGoeBa/lH3qy+YsdAoGBAKP4j65LGaf+pAdQHIyCAvurNAvOmyKTx4VC
|
||||
BMVM6qfbaQWxN8LPHRBvNQCEdWkQgI7bpmH3J0+0RFcvQsgCq0ZuOnmOM8DS7HnT
|
||||
jXPLujASkGKCCY7LRsn9FvzJp7rEH41Zav6hZf6PJjoJLUl1oP8lK7xWDUBf+yeJ
|
||||
7JHKc9NlAoGBAMhca2WXdxX1R61zdjqa7Da9fC1a8cdkPHsN9CA6ZMxGgfrbZHsY
|
||||
YbqPhjuupOJe5B5MZ12oRqye3u6SuzoJMcSBOjKMLX/ME5fOxNRiAGgm1WEbDeFJ
|
||||
3+ZyymShyUbpRLOg4fUC1vptzSJ1i93rXOZI6Vs0rWMj+PKDEbmFLiT8
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/test.com__10.0.0.1/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/test.com__10.0.0.1/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDDDCCAfSgAwIBAgIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBN0ZXN0
|
||||
LmNvbUAxNDk2MzMwNjU5MCAXDTcwMDEwMTAwMDAwMFoYDzIxMTcwNTA4MTUyNDE5
|
||||
WjAeMRwwGgYDVQQDDBN0ZXN0LmNvbUAxNDk2MzMwNjU5MIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAwRrWvzL0zR9OFg1ipRU+Pvd9v+WN4ILKxU2jxlut
|
||||
vZOevsyxflzRNOQImejpdXEtA/yZTKNYOdU+dMdPDdSsMPFzRgzQfiJDKqJw4jB+
|
||||
SF2XROuZEYMjxoJtueAGnw1DO9tgzn40NtHrPJNKadrYljEF+zycNLcPf78GK/ug
|
||||
+5rK5MkpNVmxO4yjqxk5ZB3KD4Gol3RaheVKYmTfjNIAO0wk8O2NbvG/JxOPTpDS
|
||||
aElgpfdy3Bxv4JJpjgNKrHEUYUqkwlqmrinTw8MSfKJq0MJ8nIx8HRojrYrf6Jh8
|
||||
zDpPYuqlUygcwSz0sQEY6UceaupQmlvDT9QLLVBm6aOibQIDAQABo1MwUTAOBgNV
|
||||
HQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
|
||||
/zAZBgNVHREEEjAQggh0ZXN0LmNvbYcECgAAATANBgkqhkiG9w0BAQsFAAOCAQEA
|
||||
jdyL+g1UlZ9UsnYA60iu3n9O8nzUU13z6d3TR3vFBiaTAiVYgcEoL5BmhZgn5jcW
|
||||
AlQqmVicma4rypK50GZL7/fJBwiaUjkGNBh4ybfO2TRyXbmTCtMg+1FUe/LptxD4
|
||||
IWvJvf/Y7Vv/Lb1Gh0MXUcNeyLThletz6tJI3NAQX3meZm2J6/PBoERP7KXo7C4F
|
||||
K21/V3aD8Zj7gs7LQLePz14ARAuovd/LDFmAHBcL3EIXiegDGe2c34IKupTPiysE
|
||||
KIWAgOWSBQ9iZw4Ak7eFSDPIO9Xj1X+D1Bcb2EMcYw/9xJt8JFAqFFjMHPcKxCkQ
|
||||
oY+IHfc38TMOEeAr8N+W2g==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/test.com__10.0.0.1/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__10.0.0.1,127.0.0.1/test.com__10.0.0.1/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEAwRrWvzL0zR9OFg1ipRU+Pvd9v+WN4ILKxU2jxlutvZOevsyx
|
||||
flzRNOQImejpdXEtA/yZTKNYOdU+dMdPDdSsMPFzRgzQfiJDKqJw4jB+SF2XROuZ
|
||||
EYMjxoJtueAGnw1DO9tgzn40NtHrPJNKadrYljEF+zycNLcPf78GK/ug+5rK5Mkp
|
||||
NVmxO4yjqxk5ZB3KD4Gol3RaheVKYmTfjNIAO0wk8O2NbvG/JxOPTpDSaElgpfdy
|
||||
3Bxv4JJpjgNKrHEUYUqkwlqmrinTw8MSfKJq0MJ8nIx8HRojrYrf6Jh8zDpPYuql
|
||||
UygcwSz0sQEY6UceaupQmlvDT9QLLVBm6aOibQIDAQABAoIBACeo73oNaSHH0C3P
|
||||
SfdFyab9BaKn7t+xfRvQulY+9gv9iZj+SWX+gikuvGV/5JLuT6SF+KY41iHqng01
|
||||
8hKRH1xd+qLkdt2xA8J54l1SQF10e2D4UlO6b1qR5x9J15JLEwf0IonGecrYikvC
|
||||
pIHhJKKUJvpWlG5vOouuHAJkh8ekwz17cEle6IOPY+p9hiivk80npUzbdybnHK26
|
||||
pNGPICich8sUWNaiSUia/TCwIDzU4NkNhsOxoV/QZOTOFnHl4lR81dSeptdVusPg
|
||||
JdC0+VCMc8wGgXdDqWxPzDdT7CIGNZcqpltBEe/27QPVYxckK3kDkOFRXshD5lPv
|
||||
56USHrECgYEA9pB5ChHu9JqkedN8QgUfipl/doAOz80vghG0gi+wvkeD/XrzXaES
|
||||
rXlkGJFKbPkTGhemSzBPQNEbpQa8Y+h+GfKLHux+Kd7pWRym4vXojxeJyaCCG73i
|
||||
d2qtoHi5QeZOY36AJT7bq3T1ZKHG157vEXRghth+ozpzb1w+KeO1ZNcCgYEAyH6j
|
||||
FpowWYdsPljqx+7SfoPdes/LGtfIWnGRM12C6H2Lb9FX6l1yIMNJQgYhyRPSbifL
|
||||
uArj/Jf9LskaaRYHvjXbtBANHHU92kBZszMj3x437otADwL9iWsV/cyIDkei5QTl
|
||||
fP//M5RSZj2WyRQJ8hXTA7Ya5e3rGJYlHQ1aRlsCgYBJMK2dXaFvHpCAUVTrTBYG
|
||||
0HXTuUOsT54woAzTMFDoytXVYq/nNS8UK5qY6FgNbQpMjoSggSClfu0T2aIGjjcQ
|
||||
gLznWxBAYZknCKhJavGzuCsAnRLCJWWaSSJtJijn9POD+UMUy0nt5XQKgTNDQjx5
|
||||
E/CrVoyQ64LkpZ8WVC++VQKBgQCLUMu8cem03FAPxrNluAKWHMTyiJ8mCNjUV+PA
|
||||
YHMNX+dbDIlddg9OysQF18L0OQzYtFhvi0m+hFJOhzkN2lwJBN2kch7aLnGLTXnG
|
||||
9nsvl4zf+ezKQZaxPTLrx4qm+YosP0nDoRLQ4XicSKGVGZKLoDSfeJOaP8dDr1kc
|
||||
peGbzwKBgQC3Tz57NVbIDa3SdMlpgV5iUj+AQ4Q4OLxAWzePttic1+nalQyFLMBI
|
||||
dq4HytJEen/CK/XwelHpMfMv/t2bZcqags+Epm9Qhd7jLMaFsmrscmdjXdp41kRR
|
||||
KOjZHBUuhK7HfeBLJ7oLGDlXHvtfwC59DRLipYrtJLG4Etkcjfav8Q==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDDzCCAfegAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTQ5NjMzMDY1NDAgFw03MDAxMDEwMDAwMDBaGA8yMTE3MDUwODE1MjQx
|
||||
NFowHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE0OTYzMzA2NTQwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQD8AqsPL2/NyBR9LSPSkLedA2pI5DeQ+z7Zfp9K
|
||||
8EmbCcawXZabrkPCr0LwNyylynssZXqGLYWvwpl8zGk0E2Gr++diIg17uZ4lZUKN
|
||||
jYhGdV8Y1Bx2Wl1At4LLe+bmxv01hslT4EDRzHfZU/wy/gTtYMJLuQOKqddW3lkV
|
||||
FXNgJOVGmIm1tehwDCJhL9NIyQcy6w4i86e5B8q+pk3V95mOlKR7lHhk1ZFsMCKh
|
||||
TCYG/qyu+ZUkKvZCoyV9SwuaN8Df3TT6MYSX2ulvmze2okfePwTeKMpwudrfn440
|
||||
0TWrWKJuMdNe9bHXsvhvPZlqBCouIqPC1KiuMg+Pcx0laz9BAgMBAAGjVDBSMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAAATANBgkqhkiG9w0BAQsFAAOC
|
||||
AQEAXHWVJ8fuPrF7Bgj3i7fyH0wE6mHeUMSy/bxXdbrkY/H1wGMxHZa8uAodXeNa
|
||||
Nb3pBzroqZcVWjkh56knGgeZE4vmkWWZZ3G0Bj5jr3kzhZ9Tpd1LyFOhIdUttHPs
|
||||
DOVfD7AIl3v4xlMDwlYYQKrt1LzHZX1n7sJHJK1s5pL/lB9PdUGka6XLXo7meKPL
|
||||
KluKBF729MfwBx/GNrQEh4Hw9/KVnBisKKdWqowrKzVzGj3hv2q+wCxDuUZyl7Cs
|
||||
nw07CGIn+t7+5/jRyEoIwUiOydvaOFkXgOpWxHQVEj+7Hw+rstDbMDQ2+p4LpJjC
|
||||
bLBrGtpaitmhTPyT+SSR7AZisg==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEA/AKrDy9vzcgUfS0j0pC3nQNqSOQ3kPs+2X6fSvBJmwnGsF2W
|
||||
m65Dwq9C8Dcspcp7LGV6hi2Fr8KZfMxpNBNhq/vnYiINe7meJWVCjY2IRnVfGNQc
|
||||
dlpdQLeCy3vm5sb9NYbJU+BA0cx32VP8Mv4E7WDCS7kDiqnXVt5ZFRVzYCTlRpiJ
|
||||
tbXocAwiYS/TSMkHMusOIvOnuQfKvqZN1feZjpSke5R4ZNWRbDAioUwmBv6srvmV
|
||||
JCr2QqMlfUsLmjfA3900+jGEl9rpb5s3tqJH3j8E3ijKcLna35+ONNE1q1iibjHT
|
||||
XvWx17L4bz2ZagQqLiKjwtSorjIPj3MdJWs/QQIDAQABAoIBACnOZ4JFZE7G+l8p
|
||||
td+gklVB08fz1CQBChQapWJRKmTF4mgvnmSFLEiHupsPDCEUzgo3a3QG6T6XEc9a
|
||||
Obmh3Xoid+gXrhTwXgPmIHxLFmhZ4ETGK1KW+xFZYCFRlKSELk1K5us7MIWdXeR3
|
||||
wkbTTQ30F0dokeXW9xo6GJ0eyd0Q7ZHzePI+Gh9ORLNeRdyk7Px6cFuDtox78r7b
|
||||
teQMGNeEWtdJArWIbjxPprp84uo4qb876W2TBCt/rrFp1CRfUXtjSYQXTEsvon76
|
||||
is64X3lQhZR3HCbtjbRVhq76aNJQweVIO9CHkeLwyzGc/PyblNJNM1blIWbgQHvy
|
||||
Zr8kYAECgYEA/0e4gCGohQILo4E1EnhWuHwKPACsBHkQ/D3JT2H/yo8IZUvnsPZJ
|
||||
0iNhDnnPi892Xe9n7q8LG+9FVO8OYghfiF4n66dAEO8J0UFsA5CLNGr69noQEUvq
|
||||
GJdOvX+NGFOzCaoX8ld3xTt9V+pvMFVPFKIoy6bns4PJlllG88vgweECgYEA/LiW
|
||||
UKYomY3T8e7KgCIilkNrKWYv2qCKgoXMHerI+gz9BvcSdDRz7GHpkV9ZP9GJuJ3n
|
||||
uLHdUNkFam5jtpwfqjiBvrINgqu8/jesPeocxQwJf3x5iGazxUCEK5YonePyuxih
|
||||
e3pR+ez1b3wjBfVshs8tg58F0OdO3qVu+IZM6WECgYEAz+Ie7DD8o7vjsHWSu4TF
|
||||
0hwo1W4Spt3UKCVEn2GFxPUveBYVCjW2Lerjqb8mdYsvsBU6hNOE722s/xphkxGQ
|
||||
gmNyjOO5N1EBpKdrB4gY5EnOLxN9rCgu7XGiDI0nYE0gulkKgS4becEVNJfTjf+a
|
||||
k4nuQNGdU8GZUtdHAVfgouECgYEA5ksrJxkTPEKjFf89ZyA0FmwfVh15lCfhsGlO
|
||||
hSCzvjFhzhwOI/i8fncHCeMyiFZgwuyaAZOLkyYAqmkn0ntY25wcl3LXnvDzC1Ie
|
||||
UllpmCKM8TCz6xlzzlcpWjK+lornYZljZgskcrR9P/eGTuLD3+Ehn6Sk/yYZmAh3
|
||||
JF/17GECgYEA2x7fCZN+wr+CmG74GFdNnghqOkQQ9JleGOseHM+JIRD3J3WkuZ9x
|
||||
Dfhe68OEaUBsmB3ttDiHQ5mS+YyXIZ2st6/0KQ7v6jEM3CwUoq+SZUqV8FEIBf0+
|
||||
r3yQI5sB/ZxWvsshLtlLUgdAby+ava8mG9M5RwUjp8kdmzlkCa54cc8=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/localhost__/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/localhost__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTQ5NjMzMDY1NTAgFw03MDAxMDEwMDAwMDBaGA8yMTE3MDUwODE1MjQx
|
||||
NVowHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE0OTYzMzA2NTUwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDfzQINLvjB3E/ea9+PW5SLys8Q4JqE9HUwLTJY
|
||||
obQ6jqf4BTxtQWRpyu+uO3ALT+oCYifZGSlyYbBnnrCNClbwZYAHT6OkDD7f9w/q
|
||||
xCYMosgvZhCCILHfolCYdFLMlYQg+pz4O+ct89J1pqyhEQBZRkUTLrXwqNUOfT1a
|
||||
uBPbIkXBlBn9yG3c9zj7uRiSEiDA6ZcfxDLh2RCEjZezUfL0EExU/ZHCVghew9cg
|
||||
OQ8T9dpMzCSQf8rG/higV4BZA66KCC+ETm2VXkqgWxJre+bnELHGXbiQxP5t21Jt
|
||||
ZXjjYcUs1YluS0nLBJiZdn1lYGM2Nok8JAKzg19XYnlsPJXfAgMBAAGjTjBMMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAIAyV
|
||||
xfmc5DMKgPUt2y6GLICybnI07SaNdzZf/5L3nM9b20Q26VLdZK4TbFsBhR4lfFPU
|
||||
F4oVwvYbw1bPT9u9RSy0wLRz8a/mJJX/cmh+OBhYJf7nrGuvYADHfi8zhfvnmgIG
|
||||
6PFezabzKgz5jkr0rbj32mP6sPpN1dzeDWESl7rcCfS1UHX4dlCKxnRqAYUXS4Kz
|
||||
f4VMQV8y9Q00/D9fH5xLxg2jyHVc+LfD9tA1r0EJKCB7p612k96YGFFqYqxQOYmr
|
||||
/Ut+EN0w/YMHuzMjhaEi+9T6r/VMYr3l8B8U1z0PYPFbbvMCGZUf53cBEjjxjg4n
|
||||
sQgzzTc/yfYXGcVVcA==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/localhost__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/localhost__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEA380CDS74wdxP3mvfj1uUi8rPEOCahPR1MC0yWKG0Oo6n+AU8
|
||||
bUFkacrvrjtwC0/qAmIn2RkpcmGwZ56wjQpW8GWAB0+jpAw+3/cP6sQmDKLIL2YQ
|
||||
giCx36JQmHRSzJWEIPqc+DvnLfPSdaasoREAWUZFEy618KjVDn09WrgT2yJFwZQZ
|
||||
/cht3Pc4+7kYkhIgwOmXH8Qy4dkQhI2Xs1Hy9BBMVP2RwlYIXsPXIDkPE/XaTMwk
|
||||
kH/Kxv4YoFeAWQOuiggvhE5tlV5KoFsSa3vm5xCxxl24kMT+bdtSbWV442HFLNWJ
|
||||
bktJywSYmXZ9ZWBjNjaJPCQCs4NfV2J5bDyV3wIDAQABAoIBADoAbsyt5MiRaZ2y
|
||||
hXEpAfms6Tdtjn5wwMJopEi2N/iJKl0sNjn2naiRcMbOHr6/yl0txTgNvmEnkkl4
|
||||
pjcXe+q305ORf06v4ce3x0dH6LiTo6AtKpAfGxpjfdHVK/Rbt6+4Y+At3j9Zlqzp
|
||||
MopX1qhMfvB2xa6GgxlLOhB97p926VtKYOEoDke8uP48HJTabeiFKpn76k+0HwS0
|
||||
J58Kb647DM1rqGpZW17dPb2ovi2eznATMeAgveNvVaXipUCsqnrJI+ywrLP4TBll
|
||||
Mly+DZ42e1YnM+rd0tzgC/YNO8h7lF3YnthdQ77h3Yw09cvtmHPEuURgGiHIH5IY
|
||||
HlYKBEkCgYEA7KXOLnufN1J7QG9CqsWhKI+ZBGGTd3LmXMHTM1tLpFcQaRNd/kxg
|
||||
NVo8y4VEpCdatKj+yRe3wPZ8rO/Q074Rzys4VyTP8BNn9YjtttbP8zBDmLqkXAAc
|
||||
xVKNDiuFQ40HSIdexZVP/YVCp210q6YG3OPoT4W9QvjfPHMfzW+2euUCgYEA8hpB
|
||||
PMyl/RL2WHQHdKRRss0k8AWjtoKzepRkzrwptzgFOQZSmuvfdiPStp126iCinzag
|
||||
EPsCo3T/1E1Fj4LgOIPhRd/ibXYSAParxmmx3rMTrBwo3TRf2c9nZPKAygsMNF59
|
||||
T5ySJzU6tWE0PEtCIITW+WnQeORPzW3vE0nSzXMCgYEAwHyBP8foEryxahDVvkgw
|
||||
NOjQgwLHS0KXY914Z+6bkVoMDBvnuV/wZZU3nCeTVJ56JjbLv0/edENJ1/Tc/E4+
|
||||
+C7kjeVwiYuPZOVyzeTekqWwZftyky64ODxUZbE6nV6KDRGnIbm4rbMGK19ZpgDO
|
||||
1j8/B+f1sRLzqZzOwnBako0CgYAVSR2nPM7zOzGRgVIOfUv5jnPK7pQAPemNw3JB
|
||||
guWgCDrdUqL8n/RbrLaf+6Qy3X+uw9y64XbP7D956PlOrtEL5vxtaku8F/9pOkbM
|
||||
AQr0a4Y1QZyOJkavHb8XbwLHwMTn7c7Sqw9mhxWMUOmIy40a0tMbxqPfnSMvCpVU
|
||||
es8f5QKBgQCeFljjAnEQpuUduug/DAScAjTEbJD/18wAu1PuIM/+/zFEVaOs5w0z
|
||||
uHiiPq3fiaaoBQHZofd8mxsugfLYftAP25Qw8X/WgWPg4CLGz3Br3HgnV6RyWXr6
|
||||
R600l5lWe/ZU5epjEsToc20H9z9TbXaSKWMZiMIaMnAeboSfTtQY3g==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com__/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBN0ZXN0
|
||||
LmNvbUAxNDk2MzMwNjU4MCAXDTcwMDEwMTAwMDAwMFoYDzIxMTcwNTA4MTUyNDE4
|
||||
WjAeMRwwGgYDVQQDDBN0ZXN0LmNvbUAxNDk2MzMwNjU4MIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAtNQ9DkyC1Dza3OYBLcCPeJNKC8BvYVVTL+FFzpS5
|
||||
AEPNdNA/Ezk42SBWNvaY+HYBrZniOSVMkTPFT9i5sT8bBBrUUvwGk1r6tpXDlHw3
|
||||
/S7QThqZLkGcAscmahp0mWnIIGcJxZBVMR1cfFbYNumNR9UyjUDQumCb8hlT3q0n
|
||||
pEH3CL3jRpdcFolczotUzGk9u07v81gIWgQVCyYYu3WuuRjviLq8SVoHlAWvVsdW
|
||||
rGcG1LS/OKAgECYVUxs1Qauw5VjlXPGkY7gOOnjNcl3IHZU2+7dGFw1gBf+gEC5E
|
||||
/qr2fI7Lwx7R71sY0PANuuDLGfrCAaCjG3HZ01MC5Ktx8wIDAQABo00wSzAOBgNV
|
||||
HQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
|
||||
/zATBgNVHREEDDAKggh0ZXN0LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEApTrZW/xo
|
||||
EMiNgBnRyqR0wHXpuQS/V5h75CtDKE4/Yl57cQlh30SX10anRgNpeBmaZjyFQp0P
|
||||
3MAQoxUh8rOeQBcqZp7TiIXAk8w4rYWpFx59GGgiO5yD1vOj+9cIvAFDR0Fe1kbb
|
||||
kLo8s93l4yNFTdxIIIDMgNpPcp2620wkecR3PwRV1jlM/7nMfgfRJagN2+Xq7mKC
|
||||
ELzGatuXBpiy+jwCirmgmE8/MxbFbsR9L4EdW1kdYhRT+rnSNIEN3zI8GXWIH9Hu
|
||||
91cGTgKiz1znXtlmLAaIoGTahv23YUV7tbQlrtZDZjpqvTN6yM3vS0HIoSxQnT2l
|
||||
yizQSNuYSUD4HA==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAtNQ9DkyC1Dza3OYBLcCPeJNKC8BvYVVTL+FFzpS5AEPNdNA/
|
||||
Ezk42SBWNvaY+HYBrZniOSVMkTPFT9i5sT8bBBrUUvwGk1r6tpXDlHw3/S7QThqZ
|
||||
LkGcAscmahp0mWnIIGcJxZBVMR1cfFbYNumNR9UyjUDQumCb8hlT3q0npEH3CL3j
|
||||
RpdcFolczotUzGk9u07v81gIWgQVCyYYu3WuuRjviLq8SVoHlAWvVsdWrGcG1LS/
|
||||
OKAgECYVUxs1Qauw5VjlXPGkY7gOOnjNcl3IHZU2+7dGFw1gBf+gEC5E/qr2fI7L
|
||||
wx7R71sY0PANuuDLGfrCAaCjG3HZ01MC5Ktx8wIDAQABAoIBAQCE4IjVHwfsKaOy
|
||||
OxmWoy9KLQF2TTwLTAXK40DGoqanOSuasQQIfj313zs7PE4AFcb56GBCGdR/xokx
|
||||
OVKKmpKhm412RVgXo/0jej2zuOvzkhjjq8vwH6oiqHDj+vi/WuoCVXTqxjqz0p2J
|
||||
iM9s2oZEyLdS8NPwgNlXN6FXuemtWsao5ramsG8c0XM08zWazkrLGXf63dsNlEoL
|
||||
9zzpP2nNx1DVVDTUhN3mpJE60IG0niBE1X3CuIrq4A/hIOut6qa9VgdOtnnwsAyG
|
||||
hB3ZITeoaRowedmREUSSuzXnWkfHSa5yJoRlhje4mV+RRdhZ+1DSa1jzn1ZtZhjV
|
||||
1/6KQWOxAoGBAMdTlTm+9c3rtqZVgva1WfwRpfd2B+HAy/LuBQMSNEmMXn5ooJSK
|
||||
05eXMoCQC/+3fArCjYl+I7u9GHFKa4JeLjzWbqPbski5cTEHXIINGsnE9OI5pug9
|
||||
EbiZvSeimlL9Lw2BUQVW777cfz0esI+Tg514mn43gV633BWZH/AP3LHtAoGBAOg+
|
||||
R2EnDs60m0d0apR4kuZkoJDfF+uYgqyD+SMDDzgBSr4IDvWRzKAhYn+R1HsWeOxi
|
||||
U+8kx5zmDVw+GHg397FcTIVfnuQKwmBZU/XC7oKVu6wshMXFbXUSQMmIuDbu5xmZ
|
||||
1uZvMHtdszDwBWn5zGRSM3oXSQcwburJ6f1KSbdfAoGBAJE/8jUzV5GoG+BX3eiD
|
||||
vL/36FZMt2+l+7jaA+L6CAo0dMNu4N190LTdpBqVXXkJryV0Iom31rg/EqmzzmYg
|
||||
1VbV96gGN4PRRo+wypOmkwfHM6AK3PrNTbWUqodQcV7aSshvnKfkcy5FJZ9XVtg5
|
||||
aaZ9tS8WqXppRsKiY+ie1nb9AoGBALtl1Y8NRyOAFHP5+i5HhLGnrPB5NAIFFBxE
|
||||
eXnI9DTKxhbExd1dgnILvkEV2RBhN732MagcU33FHC68ZF5NOu7AfXZ5i+qSy/mW
|
||||
2urKGk8Ap2jsm05CWmpHCkQUsCCTcaL0wbU7LzG8j+UDKDs1N7oTojn4JBNK/5Uw
|
||||
zEB3+zedAoGAYAI8WQm20ElzbBB8MqL31zM3LZ5Dp/GIjPWhjwEOsGt2oCSrLxC0
|
||||
xcYRq73St5RS78uGSJxAQaMQna52idifxRE6D3IyAvsMAbn1q+LCqST9zy5jzmO8
|
||||
tUNvhnkJs1jdDjIzWVtbtMWb7/5wWANx5989B1+QWU14Qmb7uziMVR8=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com_star.test.com_/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com_star.test.com_/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDEjCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBN0ZXN0
|
||||
LmNvbUAxNDk2MzMwNjYwMCAXDTcwMDEwMTAwMDAwMFoYDzIxMTcwNTA4MTUyNDIw
|
||||
WjAeMRwwGgYDVQQDDBN0ZXN0LmNvbUAxNDk2MzMwNjYwMIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEAwLNxhHFPJFGnDoOnt/ELutNthoJNYLwus52+GLD1
|
||||
VYDDBA7766rzAxK8wGF9vGbXZX7L1uW3VJyJJLzZR1bBTdeOWXpyscz+33+jETbn
|
||||
Eg2Dp7KbdFAFw66B90vxLbHKbQtH63VtNg9lh+d0K4QI6SGFlI/Mv9VWawpKk1P5
|
||||
X1cgl1EgR5e4kIgQsrkO+MRc0SLZG/s9MvThrHVlZLWPjRaiqk1GDxvBjfcBoPzZ
|
||||
0jOHhWLJGWZcwXZ5brqPcqn+YMceXQlxrjxJvyq02DEWjtfimu7qoZ3+fgQy4rJ0
|
||||
GzPaDvwnkwvJQ2iN59mcybfg6AoblCOt1ypIqouMrI/J3QIDAQABo1kwVzAOBgNV
|
||||
HQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
|
||||
/zAfBgNVHREEGDAWggh0ZXN0LmNvbYIKKi50ZXN0LmNvbTANBgkqhkiG9w0BAQsF
|
||||
AAOCAQEAGQ+K0dnTaX5Ry9PLA6M8yrMhq5gnX2RfyIMXCuFfMqjdju4BkTR+6zm2
|
||||
El8Z6glQ6QsKYHR4XhlhHFOP+gGyaTrsDSV2qCgphJmtn6QWOSfEmBRNNCTpMXfS
|
||||
5Ek/2dXItMjmnMdDOqcLofQyIIQFE9VpLyaFN0n1w9k6EscwSxsMiBVFwOhHpBex
|
||||
BPJnrKBDWNVHjgocUI3YzN2TzzRxVxB/xc5+Sl/jnpguad+q/wjFgpr9p2a4yAS7
|
||||
W5bXcA1S4iSp8uKVv0JM/cfFlF094ft88A/SIt8Sn8BmeOGQtSk/sf5mFbr7TRqE
|
||||
oDuKNM5AIM/fClQdlbKo7xpcJCiRkQ==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com_star.test.com_/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost__127.0.0.1/test.com_star.test.com_/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAwLNxhHFPJFGnDoOnt/ELutNthoJNYLwus52+GLD1VYDDBA77
|
||||
66rzAxK8wGF9vGbXZX7L1uW3VJyJJLzZR1bBTdeOWXpyscz+33+jETbnEg2Dp7Kb
|
||||
dFAFw66B90vxLbHKbQtH63VtNg9lh+d0K4QI6SGFlI/Mv9VWawpKk1P5X1cgl1Eg
|
||||
R5e4kIgQsrkO+MRc0SLZG/s9MvThrHVlZLWPjRaiqk1GDxvBjfcBoPzZ0jOHhWLJ
|
||||
GWZcwXZ5brqPcqn+YMceXQlxrjxJvyq02DEWjtfimu7qoZ3+fgQy4rJ0GzPaDvwn
|
||||
kwvJQ2iN59mcybfg6AoblCOt1ypIqouMrI/J3QIDAQABAoIBADlQ5lHTdgvCcaMC
|
||||
i89NVgTSwEAC38sHDgkEwTcM41v0OY0qe8nkBfYDC7tZBBIWsLiSl0Sq/PWgvbX8
|
||||
qc/rQQHLPJ7mqdiytZh6mWEUaHRZKrClklFtP9mtPSD2KJ1DxYbblR+2xK4QtE1R
|
||||
DZ+n3RKikt3WS7s/VD3wSLA7iO/PMJ+ER3kPxZWsVYJw6KpmR8U/v881eM7NGoK3
|
||||
SwYSqXfcRidNOl68Je3vm3MpK7SiPEOX9AGJUXlkpjJcyOqEE+X23wNSZUpKT+i7
|
||||
NIrXaEml1nKlTRMlDWi5ZPqzhpguc5Nmn0Tu+01ZClwcu+/7b4C5tkIjXQnyjXsO
|
||||
0o1EqcECgYEAyEJwGl6KvvYaRsBiFRWIN1950S87O0grOnwb3/FedW4CEf9Lm/Xj
|
||||
653HTe3zYAJDlsRFCOQ/KXi6nUjAAv8DAydKWmr5EonNj+VXT0Sw32N681lDMPzK
|
||||
4csJek2bWt2alKbachKAq/gr00837HELl1ALr1X3a3HXr6hZHNjUHpECgYEA9lZq
|
||||
tXDDV+emyIf/O9LwDUkctHc06qD+uBfiuW0DqVeYPE7E3QPsPzyjnwSfqmgYlpKm
|
||||
Ku60MfmAVXT8kUSBUuY9ph5AOE/5LwkM3ONc34UBtkruzHUccGXuXelUzmnAQLhi
|
||||
ZJiWJJrHYICcOZNzd9UmqYye66lyzp9NWe7ttI0CgYB8qYcZkzLxz0fqNUaZc275
|
||||
cXDmvG016SYn4SyFCr9PgKy/QVpy+u3oNa7lHzR61s32Y7XJ5DRBf8tDsA7/jnn0
|
||||
k5fgk2j7llcltZHVYaBtX4MhS7bdHvC5AGik/vyv4vWghgHEMypwYyv8/fqFMJYT
|
||||
mpu7iVtdQmAPFFBs+bmKUQKBgQC4Hm8znkzjbymhyMRkmo0B4RurZ04N8LdyMags
|
||||
n+aUs/v9V4KdD0mNGm3RdUtBuvxlzsaUYEYe9DWPqhj9Gw7uP1jkyMeHT38YQNN0
|
||||
ZhwMlQVqkydZek7/U7COUNAqV7Byr14bglD2mxlab8ZU4njzoEV9hLVjvwsolLjj
|
||||
90mEIQKBgF/IE4+wL9sdeQ7MGanYHFzQtyKxWnRzCGWDkHV9InGZ2I7O3AeqDC76
|
||||
fXhiRoRbBMSgddyOYTUoaPhwgqPGoC9H/3ZHnyx5fokl0FTNfzk1mnY1n1HD2roR
|
||||
p76hbMs+TjIGG+lNDso3WuV21Gn+vynDVO9FQumgeaEc+CLXpwEq
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost_test.com_127.0.0.1/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost_test.com_127.0.0.1/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDGTCCAgGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTQ5NjMzMDY1ODAgFw03MDAxMDEwMDAwMDBaGA8yMTE3MDUwODE1MjQx
|
||||
OFowHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE0OTYzMzA2NTgwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQDO4DuzjpGF3Em7sIrAv6/19KyTVLxV4jM0XX8G
|
||||
S8Uo04oyovHnHqfCDd+yaGs1glxD95eqZ5kZDgQNXhn+oZkNvcPJJ8O2tPhHwvjC
|
||||
PR3KzNL3dFMo6+7BCmBRIVvSNHkkZ5lyoOSE7deGvoKl3KuEWUGwUiGnPx95snCD
|
||||
HPfgHYto1zSI2NbdnF4hRuOiSFhettMZiwpXEYGhcMh1RYDYxPf+5/XcFz3VCdpF
|
||||
1COXSYIS2r60H08dH1u6rO1iliJaRh+jqk998exdi0TwJvVTiaubMRZxtGkPNxjE
|
||||
tSukjDA9UaqAFBnireXj2qc+a6B7AMsoTymn8Ka6QJyt+3ZdAgMBAAGjXjBcMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MCQGA1UdEQQdMBuCCWxvY2FsaG9zdIIIdGVzdC5jb22HBH8AAAEwDQYJKoZI
|
||||
hvcNAQELBQADggEBAAOc/JL/2xfnPAcwzxvF/ssYK6EpjD6plG+OhlIj5UiXo/z4
|
||||
5IcLCg+N1P7dGaiSMfI/hIM+M/z24QMk6gXFMrVUFh6sm49dNmTry4Qsyp8Up5HD
|
||||
ettKLaWG3YA08VeqJ0C2xsFh7nJnrlzQad1dwtnj6ActWx/2g8SFkfpQYLba/YI/
|
||||
YC3biwKGY9P7lmCP5p4234blenJHJW5hbwNyLManvEA9O39Rr4oph2Z1YtmpsNM/
|
||||
p+xioLTx7oFMtRkBBpmzSKJgLAqCxVg7P+MzjS1s3W49QLI8F1sZ+mXBrZxbALhQ
|
||||
TtOjvUx+vb6Gihkzn8XkGdbgsiOTMBoT0EmIBJE=
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost_test.com_127.0.0.1/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/localhost_test.com_127.0.0.1/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAzuA7s46RhdxJu7CKwL+v9fSsk1S8VeIzNF1/BkvFKNOKMqLx
|
||||
5x6nwg3fsmhrNYJcQ/eXqmeZGQ4EDV4Z/qGZDb3DySfDtrT4R8L4wj0dyszS93RT
|
||||
KOvuwQpgUSFb0jR5JGeZcqDkhO3Xhr6CpdyrhFlBsFIhpz8febJwgxz34B2LaNc0
|
||||
iNjW3ZxeIUbjokhYXrbTGYsKVxGBoXDIdUWA2MT3/uf13Bc91QnaRdQjl0mCEtq+
|
||||
tB9PHR9buqztYpYiWkYfo6pPffHsXYtE8Cb1U4mrmzEWcbRpDzcYxLUrpIwwPVGq
|
||||
gBQZ4q3l49qnPmugewDLKE8pp/CmukCcrft2XQIDAQABAoIBAQC4LgSZAKXook32
|
||||
zt9JaZ+T/yih5r/iSofB6DeN+GloxqT0HgP1+2Hpr4ZoBPTl59CTJlyd9qvkFYlg
|
||||
CVZlT6O/CyLC9ScWhBnDcf8cl2LtydtNOgBLn3mp3b4QnP1tsQ/lbGK+HiY1frby
|
||||
nKweVM9S/lAwVX7mspkoNMhcwq6glLL1QnJmLfmHtsOik4e+eD051aYI4OcfkFFo
|
||||
T5f6Jg5oNC0L9x+ona1avfD2eBmXmUv8kV4+NSeu+pM+hWnc6r5XnHdXgo0Fu+nf
|
||||
CQ6wUkyXQAmANI/qo+73G3lEZ7KA9mqrCTbM8sulC4c7+Qpb1vHjuz7pSMj57k67
|
||||
tI2lPGW1AoGBAPDdooMFZIq6n7wt160MlYDhLujcPaMzgiXWPg9TOLzuWrSZleGH
|
||||
k818LTXKlQP2ffVADlwHvyi0E7yZVzmG2Xt6Gi8lkGIP5LZK81BlsBspX00bvJCM
|
||||
VeyE6flSwLA86bQt8spYNAOMMc12F2i3y+YkoKfQmgMiG3H7RjM4Yeq3AoGBANvf
|
||||
3bh7iHs74oZz40em/W+LdAFBE29FeCloqIXnueHVTLdpHFepmp5OBxogkaYJlVOF
|
||||
0B/cl8sYn/6CtqKHzhCcCc8V/54O15n/yNRMru4GU0Z5mhaxWvypJM3WXTVvC612
|
||||
ODM7shXtFQAF5kgeGSDFOepUG1XRPqPjBmSOFiOLAoGAL/kkzg276bmj6PoEc+4G
|
||||
tOWkzzJWgHPY3ypdr/W2XkLnBAN3C7VSJeIuqwEPTvvAOKGqen/8feHn8G9mJQoL
|
||||
tQErx+omaLGbrwcckQ2/1pkV5yi4FN4umu4aH9Nwl2l6bq2cJtsMeQxqOu7jWLMK
|
||||
xGrVKLduhWM8riYK5Fa9gd8CgYByWA+UOxoVDGWXinjt8FfoRFuFs3QJ79S3pCfP
|
||||
y1yNp0GIOG9230r7fJSQ79MOx/kSQIqlpzCBfELoNs4H5Bwz4xHu0f/nEMzq12x3
|
||||
i5GFU2DHS8Hofz9UGhp7uTkr6p3mDY7YhfjtEihTpL3HT2+97RNbW3YDwReUXyIZ
|
||||
9RJqVQKBgEQzEKMFpA5NTzs4BanS8372XXKBRhP0CCTcxS23k/kkYFd0Swnsmv+s
|
||||
ZCJhV5B1Uz0aSK9jfvCaEfwHjdhxfyYBzoXYVxaxphwNY1Vq7QaEzzyXWKDK3beS
|
||||
9pwnZf6bEQEvvXyJ3ssqLaHWCoViHe0h/Atsj6UOYhQt5mXzZDEK
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDDBN0ZXN0
|
||||
LmNvbUAxNDk2MzMwNjU1MCAXDTcwMDEwMTAwMDAwMFoYDzIxMTcwNTA4MTUyNDE1
|
||||
WjAeMRwwGgYDVQQDDBN0ZXN0LmNvbUAxNDk2MzMwNjU1MIIBIjANBgkqhkiG9w0B
|
||||
AQEFAAOCAQ8AMIIBCgKCAQEApFk4QTS28tu20CSV1s0aCC7kV7p/V8XziC5g98OI
|
||||
GO9s8uVrzc51xsnJ3Y3+Xnx43x9G9VjgSNhPAuE7+/UnP6iAkZZV0mOD3xwwILwy
|
||||
JK6gZOcZzd9BX9JldSnjv64r5peg5mzdhNqpRs0i3oPcfriW0sGkzl6IQlsowGoS
|
||||
eDkO6+Z0IpQDo4CqcWond78LDyvqwbfFReyHEtz8D17sciGni6QPK3kGBFDiO0Ay
|
||||
keV4GqEXw13ryKzVun/haLmekRf8svAU8+pjpHY0uc1cv0l0nTIZdwnp2EIF0msa
|
||||
EKYyUUpcuFkMQTToYzj9LaIQmvAGIVIouNAydbheBifySwIDAQABo00wSzAOBgNV
|
||||
HQ8BAf8EBAMCAqQwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB
|
||||
/zATBgNVHREEDDAKggh0ZXN0LmNvbTANBgkqhkiG9w0BAQsFAAOCAQEAWlrvejAW
|
||||
DpKWBfgQpJLpL56v6dFkCjhThtvDrfvpx/gL0p2QQOIrgD7o+/+fmockmpphf8zc
|
||||
lAFS/M2IuuvJUN7qvq/4pnUa527LsQFIQSG4GhNfHHdKfISLdjgy7LRlmjLnnSu3
|
||||
9qa5KvJg9SIWpe41LbeQPB8fLYNuF+tPzrI7QLp/VbMSSyNz4wzpdk8/+r+3KmU8
|
||||
D1SEQcaEIg7/YlyJ7TZiWiFlZRFcObE11lRYo6tnh8t2e7s0IqC1JwiAmCk0j0iZ
|
||||
kHE8yND+CuO8QVDxnmu86STL+q0ldZL6C/1lLlnKFKLp+bZfW+s+kds7lDSmUMDi
|
||||
CbImL3sIf6clIg==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpAIBAAKCAQEApFk4QTS28tu20CSV1s0aCC7kV7p/V8XziC5g98OIGO9s8uVr
|
||||
zc51xsnJ3Y3+Xnx43x9G9VjgSNhPAuE7+/UnP6iAkZZV0mOD3xwwILwyJK6gZOcZ
|
||||
zd9BX9JldSnjv64r5peg5mzdhNqpRs0i3oPcfriW0sGkzl6IQlsowGoSeDkO6+Z0
|
||||
IpQDo4CqcWond78LDyvqwbfFReyHEtz8D17sciGni6QPK3kGBFDiO0AykeV4GqEX
|
||||
w13ryKzVun/haLmekRf8svAU8+pjpHY0uc1cv0l0nTIZdwnp2EIF0msaEKYyUUpc
|
||||
uFkMQTToYzj9LaIQmvAGIVIouNAydbheBifySwIDAQABAoIBAC07+UXJMomJe7my
|
||||
OCOU9BNIhMS4qhAIhcz3puzKvozOaVg5WQCeUMBUDeGTEW4EKGiQ/UDOI30pPCX4
|
||||
xD/G6/STdj+b9ycfCM7BvWVlJw/9mFvtV6bYSfALwq0KyIqbb63aEgkZ8aZ9YJYC
|
||||
JBTGiku+lpSPg8LhNfO9j2vWRhsceFEtdkv18T9sCSOQTYIrDKFdbxsl49A/D9WZ
|
||||
YERYVWmWsSHl4CkKAzrQRMIrREgYJk5R3Qt0Qk+3a4BYlmDG6/5Ayfi0sx+bpcK+
|
||||
255GbRPhe9rTUI6uIc57q7TxGOIG+ekTVqkL87bVR8+oLY/5ARW3jXedB+Th2ku4
|
||||
/JziseECgYEAx77MOmRPx5ggNQqmSfcaxozmX8Cil6pEZRQxdtdgum9QnyXhG5q9
|
||||
J8Onw2o5+u+KsTmXIx4CdcA+fL4Z9D/g6GHsi87lJT6KhdHGAmPsEVFQYc0kme3X
|
||||
hEfGppP4G4haHBzhNh5V9CxlZ7aHsB9J/LGZ2Utz+kvGHE/8GKiERNMCgYEA0qJj
|
||||
8pdnnObFW4x+/TrOzf9jxAyB7xp3ZTp1gayzmrG4cQr5OMhAZEXG5t1JOg5SvQXh
|
||||
LRWDr21BtbizJoFcAe0FzMdWIR8fz8RfqiFlfZSnMBnkhQrngu8CghAkg3g0h0m1
|
||||
tSSpaqQGVRdOFG1A/HSjiNdsBpqLGxejdUfZkakCgYEAuIAFdr4nbu6WeUf/93w3
|
||||
EEUfmjx+MudES7VORz6EC1E8v/QmLPBOXkd8U6VwLthKXI8cioRysz9CuMiDgrfq
|
||||
T9v7udv+jtto6listJKFgC2CuQPbFg+6LWZ7GGy/FPdqfGM0p19395V2iUjxpn4Q
|
||||
QkfuGud2FjEdqhM7ga9OlZ8CgYEAvwXM1NYgfnf/Jh6NkEREBygFJ2gccmazPxe8
|
||||
N4UTQUjJt6M3EBrz4rwxPXAUF9WjjY+GfieTzIWuYQqHVnVcptZ5OxTQdKtQY1F5
|
||||
XMZ6z2AAG6xvxYR1HxZ5VNEk/CIIqWTcqNH93xuwLn07L2VB2XJCWTjc3ERNwZ03
|
||||
fYZStzkCgYBtXFo7OC80f6xRDa1NfQfP4vP6dMczZBZBx2A/rCybwtQsJ40duEB2
|
||||
hGmgqIrRfh7H7KC8W792xzE1PKUXztTzxxpIr7Ho/nDUWzambMi39EYyBJ6Fp7z2
|
||||
RkPJFDkAYDpx/v357886y7HFz8pUUNd+z9V9CLemVqhjBWmIaLEX/w==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/localhost__/cert
generated
vendored
Executable file
19
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/localhost__/cert
generated
vendored
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDCTCCAfGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAfMR0wGwYDVQQDDBRsb2Nh
|
||||
bGhvc3RAMTQ5NjMzMDY1NTAgFw03MDAxMDEwMDAwMDBaGA8yMTE3MDUwODE1MjQx
|
||||
NVowHzEdMBsGA1UEAwwUbG9jYWxob3N0QDE0OTYzMzA2NTUwggEiMA0GCSqGSIb3
|
||||
DQEBAQUAA4IBDwAwggEKAoIBAQCmZQErnj8vlA5uvizHXnGmvDCpeOkc5Ldbj0Hz
|
||||
zqTCiwOSTBeestdN6oLKvxJVlvyO3UQJxhJWr00QRwdCYIvLIDmiBQjb7ciOPcl8
|
||||
WdLjBtZ36288E9P1nVwFi2RwLo/XzZlCYX0aRe/SM7/h5zzNxBR6FdRvtI1d5as4
|
||||
qiGdj7TqCFIJ/8jaBfxlusjEJd+xc0Cn8IAmSpPJYCCDmxjAzw0D12Ui3toTFHUI
|
||||
J9BA7iXTVvMbfau8KQJTPeu/HQ4zgU//4GW1Qo52gNzfY37iliZaG3HTWD1pxTFO
|
||||
qdDuprFIffsQrQ7mHAkGnAExxZYc2cE4UF+ME8dV0/6nTQA1AgMBAAGjTjBMMA4G
|
||||
A1UdDwEB/wQEAwICpDATBgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTAD
|
||||
AQH/MBQGA1UdEQQNMAuCCWxvY2FsaG9zdDANBgkqhkiG9w0BAQsFAAOCAQEAdUUo
|
||||
i0A3ttsTca5n9OY4EbDPQz7uAhXkihwoG2YeqnucrNXg+SaS+7hFA9sa1gY5NmDF
|
||||
96OvmQLBEXVz3uLEiv2KGtY5yiAo6iTtk2gtoGAloULEJ018XGQ0yNuDWUfRLyc9
|
||||
5nA8CBPEnBR7MjO6XhvvFMbAjtXoDKFqVeq3okXbL7ygUjlpwv+Sbps+DjQdmnIQ
|
||||
A90S7gcFqaCmy2KnzADartasIImy/78R9r68UdJrsmyy/aPNnJ/9AbbU8Z20JFcu
|
||||
clKOENaKJe/o1nOVzZA2E2oVElT++2wNfomDN5ae77cLoCjZPOBArFlhwyk16YIa
|
||||
wX3ITLz6Ibr9Iy7DvA==
|
||||
-----END CERTIFICATE-----
|
||||
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/localhost__/key
generated
vendored
Executable file
27
vendor/k8s.io/apiserver/pkg/server/options/testdata/test.com__/localhost__/key
generated
vendored
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEApmUBK54/L5QObr4sx15xprwwqXjpHOS3W49B886kwosDkkwX
|
||||
nrLXTeqCyr8SVZb8jt1ECcYSVq9NEEcHQmCLyyA5ogUI2+3Ijj3JfFnS4wbWd+tv
|
||||
PBPT9Z1cBYtkcC6P182ZQmF9GkXv0jO/4ec8zcQUehXUb7SNXeWrOKohnY+06ghS
|
||||
Cf/I2gX8ZbrIxCXfsXNAp/CAJkqTyWAgg5sYwM8NA9dlIt7aExR1CCfQQO4l01bz
|
||||
G32rvCkCUz3rvx0OM4FP/+BltUKOdoDc32N+4pYmWhtx01g9acUxTqnQ7qaxSH37
|
||||
EK0O5hwJBpwBMcWWHNnBOFBfjBPHVdP+p00ANQIDAQABAoIBAEqbVTgCf2BM6V+8
|
||||
Q4WtEhZpoQQL47IB1zjldfPkfrl/7T0Ggzy0AZe9A6H2pDjeNyWzjlM3jXdyqIIY
|
||||
5udbNLMCIvVDfqJl7pvgllv8RnNm2HjyhnCQj2Q8h0sxIfqu1e40EHjUD2zdWNeO
|
||||
PoYC0Z+NQIUxDox7jzs/xFLm7aWgVTYk86iFa0/uYwnCQpGHR5OkBOhVdkqBMUrH
|
||||
7hPIoKgLNHT8/jhugv+GamtAmys1KoqIvZsc/XQCMPVmE55n0OBtooQ3fhBXJC0x
|
||||
UZgJGKNLSjxt7uxHb/BQk5DtFJmPck0derMNkM8WQ6o/XSjlSiKtsct8c7us/c7r
|
||||
9OFkcy0CgYEAwB6uKr3Al1IM16t6yEEdfrNc/koJ9AQ3sVHSE5CjFBLMs6gL+Feo
|
||||
r3oFUglY1QB6v+5YUVsLovJLMHuH1x7fKHmd0Fmx75uVzurG2JdKwCiV39PPKYjr
|
||||
238Yw3Nk6IllPCn+SOIvPDhiuvPwX8FylKGPjsi7AHphKN0f9EEnL6cCgYEA3biT
|
||||
l8yvpgPpZrpTaMlotKvZkTKtb212a5EdHn0bk5fSM9xaRcumRsWd9eWGutHpMbZx
|
||||
wj1jAFaGDleBl08zHAr5jsY7i1C3CFKFEUbYR5Qu5+Ot/gdEyO2+j0ehluPOBFad
|
||||
P514bnWpdct8Khv2dW3OJMzVdFM7M06MXkQFLMMCgYAugT63qecMuhfRvPijsN9q
|
||||
5BAGnfV60+qHSb8IbWKMYWCbvHnpS47Rqz/Oc+TE6rDL9O38A4X1k6EPw1dGg3qR
|
||||
WZ8R6CRiU88Xn00y8Kxvh2OAIs1qyHIPV9yusygiAeA3iBK9ocK9WQjNKG9vPmx1
|
||||
/gW6Emzx2K3cLmqlvX+20QKBgA82yCHlcLQSlqRu6qRJFb83/5DgCliYCUUuSvI5
|
||||
oXfKNilK2ILri3hVvRYPbuRI3RiFLV8nuTBfv4kiZgkM7TPiaOdsIM/ZdQXEK89Y
|
||||
riSTPXi5/cltlEG5VpccUjE5ZnyTuOyRkJ77weoMUXPOQWYjZ77Ms2TtBSXrIOSj
|
||||
BDvjAoGBALAag/LXYvi7l4xIuUMuo1HC4YXZVSqDM7kftLdSUGOl/zGV/0QkSh76
|
||||
KCVOwXOCmzFeImrZYYDf4uu1wW7paprNaCV6EiChhRgCrjkZIpyw6RHAGshp3a3e
|
||||
BjXCIHOW5TsWRdR+IvjzIMp9tbHCEvjSw0jDm3JiaSYGRD1AldhV
|
||||
-----END RSA PRIVATE KEY-----
|
||||
28
vendor/k8s.io/apiserver/pkg/server/plugins.go
generated
vendored
Normal file
28
vendor/k8s.io/apiserver/pkg/server/plugins.go
generated
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// RegisterAllAdmissionPlugins registers all admission plugins
|
||||
func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
|
||||
lifecycle.Register(plugins)
|
||||
}
|
||||
53
vendor/k8s.io/apiserver/pkg/server/routes/BUILD
generated
vendored
Normal file
53
vendor/k8s.io/apiserver/pkg/server/routes/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"index.go",
|
||||
"metrics.go",
|
||||
"openapi.go",
|
||||
"profiling.go",
|
||||
"swagger.go",
|
||||
"swaggerui.go",
|
||||
"version.go",
|
||||
],
|
||||
deps = [
|
||||
"//vendor/github.com/elazarl/go-bindata-assetfs:go_default_library",
|
||||
"//vendor/github.com/emicklei/go-restful:go_default_library",
|
||||
"//vendor/github.com/emicklei/go-restful-swagger12:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/version:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/endpoints/metrics:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/mux:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/routes/data/swagger:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/etcd/metrics:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/handler:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/routes/data/swagger:all-srcs",
|
||||
],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
2
vendor/k8s.io/apiserver/pkg/server/routes/OWNERS
generated
vendored
Executable file
2
vendor/k8s.io/apiserver/pkg/server/routes/OWNERS
generated
vendored
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
reviewers:
|
||||
- sttts
|
||||
12
vendor/k8s.io/apiserver/pkg/server/routes/data/README.md
generated
vendored
Normal file
12
vendor/k8s.io/apiserver/pkg/server/routes/data/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
The datafiles contained in these directories were generated by the script
|
||||
```sh
|
||||
hack/build-ui.sh
|
||||
```
|
||||
|
||||
Do not edit by hand.
|
||||
|
||||
|
||||
[]()
|
||||
|
||||
|
||||
[]()
|
||||
24
vendor/k8s.io/apiserver/pkg/server/routes/data/swagger/BUILD
generated
vendored
Normal file
24
vendor/k8s.io/apiserver/pkg/server/routes/data/swagger/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["datafile.go"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
17087
vendor/k8s.io/apiserver/pkg/server/routes/data/swagger/datafile.go
generated
vendored
Normal file
17087
vendor/k8s.io/apiserver/pkg/server/routes/data/swagger/datafile.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
18
vendor/k8s.io/apiserver/pkg/server/routes/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/apiserver/pkg/server/routes/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 routes holds a collection of optional genericapiserver http handlers.
|
||||
package routes
|
||||
69
vendor/k8s.io/apiserver/pkg/server/routes/index.go
generated
vendored
Normal file
69
vendor/k8s.io/apiserver/pkg/server/routes/index.go
generated
vendored
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
)
|
||||
|
||||
// ListedPathProvider is an interface for providing paths that should be reported at /.
|
||||
type ListedPathProvider interface {
|
||||
// ListedPaths is an alphabetically sorted list of paths to be reported at /.
|
||||
ListedPaths() []string
|
||||
}
|
||||
|
||||
// ListedPathProviders is a convenient way to combine multiple ListedPathProviders
|
||||
type ListedPathProviders []ListedPathProvider
|
||||
|
||||
// ListedPaths unions and sorts the included paths.
|
||||
func (p ListedPathProviders) ListedPaths() []string {
|
||||
ret := sets.String{}
|
||||
for _, provider := range p {
|
||||
for _, path := range provider.ListedPaths() {
|
||||
ret.Insert(path)
|
||||
}
|
||||
}
|
||||
|
||||
return ret.List()
|
||||
}
|
||||
|
||||
// Index provides a webservice for the http root / listing all known paths.
|
||||
type Index struct{}
|
||||
|
||||
// Install adds the Index webservice to the given mux.
|
||||
func (i Index) Install(pathProvider ListedPathProvider, mux *mux.PathRecorderMux) {
|
||||
handler := IndexLister{StatusCode: http.StatusOK, PathProvider: pathProvider}
|
||||
|
||||
mux.UnlistedHandle("/", handler)
|
||||
mux.UnlistedHandle("/index.html", handler)
|
||||
}
|
||||
|
||||
// IndexLister lists the available indexes with the status code provided
|
||||
type IndexLister struct {
|
||||
StatusCode int
|
||||
PathProvider ListedPathProvider
|
||||
}
|
||||
|
||||
// ServeHTTP serves the available paths.
|
||||
func (i IndexLister) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
responsewriters.WriteRawJSON(i.StatusCode, metav1.RootPaths{Paths: i.PathProvider.ListedPaths()}, w)
|
||||
}
|
||||
54
vendor/k8s.io/apiserver/pkg/server/routes/metrics.go
generated
vendored
Normal file
54
vendor/k8s.io/apiserver/pkg/server/routes/metrics.go
generated
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
apimetrics "k8s.io/apiserver/pkg/endpoints/metrics"
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
etcdmetrics "k8s.io/apiserver/pkg/storage/etcd/metrics"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// DefaultMetrics installs the default prometheus metrics handler
|
||||
type DefaultMetrics struct{}
|
||||
|
||||
// Install adds the DefaultMetrics handler
|
||||
func (m DefaultMetrics) Install(c *mux.PathRecorderMux) {
|
||||
c.Handle("/metrics", prometheus.Handler())
|
||||
}
|
||||
|
||||
// MetricsWithReset install the prometheus metrics handler extended with support for the DELETE method
|
||||
// which resets the metrics.
|
||||
type MetricsWithReset struct{}
|
||||
|
||||
// Install adds the MetricsWithReset handler
|
||||
func (m MetricsWithReset) Install(c *mux.PathRecorderMux) {
|
||||
defaultMetricsHandler := prometheus.Handler().ServeHTTP
|
||||
c.HandleFunc("/metrics", func(w http.ResponseWriter, req *http.Request) {
|
||||
if req.Method == "DELETE" {
|
||||
apimetrics.Reset()
|
||||
etcdmetrics.Reset()
|
||||
io.WriteString(w, "metrics reset\n")
|
||||
return
|
||||
}
|
||||
defaultMetricsHandler(w, req)
|
||||
})
|
||||
}
|
||||
39
vendor/k8s.io/apiserver/pkg/server/routes/openapi.go
generated
vendored
Normal file
39
vendor/k8s.io/apiserver/pkg/server/routes/openapi.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
"k8s.io/kube-openapi/pkg/common"
|
||||
"k8s.io/kube-openapi/pkg/handler"
|
||||
)
|
||||
|
||||
// OpenAPI installs spec endpoints for each web service.
|
||||
type OpenAPI struct {
|
||||
Config *common.Config
|
||||
}
|
||||
|
||||
// Install adds the SwaggerUI webservice to the given mux.
|
||||
func (oa OpenAPI) Install(c *restful.Container, mux *mux.PathRecorderMux) {
|
||||
_, err := handler.BuildAndRegisterOpenAPIService("/swagger.json", c.RegisteredWebServices(), oa.Config, mux)
|
||||
if err != nil {
|
||||
glog.Fatalf("Failed to register open api spec for root: %v", err)
|
||||
}
|
||||
}
|
||||
36
vendor/k8s.io/apiserver/pkg/server/routes/profiling.go
generated
vendored
Normal file
36
vendor/k8s.io/apiserver/pkg/server/routes/profiling.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
)
|
||||
|
||||
// Profiling adds handlers for pprof under /debug/pprof.
|
||||
type Profiling struct{}
|
||||
|
||||
// Install adds the Profiling webservice to the given mux.
|
||||
func (d Profiling) Install(c *mux.PathRecorderMux) {
|
||||
c.UnlistedHandle("/debug/pprof", http.HandlerFunc(pprof.Index))
|
||||
c.UnlistedHandlePrefix("/debug/pprof/", http.HandlerFunc(pprof.Index))
|
||||
c.UnlistedHandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
c.UnlistedHandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
c.UnlistedHandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
}
|
||||
36
vendor/k8s.io/apiserver/pkg/server/routes/swagger.go
generated
vendored
Normal file
36
vendor/k8s.io/apiserver/pkg/server/routes/swagger.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"github.com/emicklei/go-restful"
|
||||
"github.com/emicklei/go-restful-swagger12"
|
||||
)
|
||||
|
||||
// Swagger installs the /swaggerapi/ endpoint to allow schema discovery
|
||||
// and traversal. It is optional to allow consumers of the Kubernetes GenericAPIServer to
|
||||
// register their own web services into the Kubernetes mux prior to initialization
|
||||
// of swagger, so that other resource types show up in the documentation.
|
||||
type Swagger struct {
|
||||
Config *swagger.Config
|
||||
}
|
||||
|
||||
// Install adds the SwaggerUI webservice to the given mux.
|
||||
func (s Swagger) Install(c *restful.Container) {
|
||||
s.Config.WebServices = c.RegisteredWebServices()
|
||||
swagger.RegisterSwaggerService(*s.Config, c)
|
||||
}
|
||||
40
vendor/k8s.io/apiserver/pkg/server/routes/swaggerui.go
generated
vendored
Normal file
40
vendor/k8s.io/apiserver/pkg/server/routes/swaggerui.go
generated
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||
|
||||
"k8s.io/apiserver/pkg/server/mux"
|
||||
"k8s.io/apiserver/pkg/server/routes/data/swagger"
|
||||
)
|
||||
|
||||
// SwaggerUI exposes files in third_party/swagger-ui/ under /swagger-ui.
|
||||
type SwaggerUI struct{}
|
||||
|
||||
// Install adds the SwaggerUI webservice to the given mux.
|
||||
func (l SwaggerUI) Install(c *mux.PathRecorderMux) {
|
||||
fileServer := http.FileServer(&assetfs.AssetFS{
|
||||
Asset: swagger.Asset,
|
||||
AssetDir: swagger.AssetDir,
|
||||
Prefix: "third_party/swagger-ui",
|
||||
})
|
||||
prefix := "/swagger-ui/"
|
||||
c.HandlePrefix(prefix, http.StripPrefix(prefix, fileServer))
|
||||
}
|
||||
57
vendor/k8s.io/apiserver/pkg/server/routes/version.go
generated
vendored
Normal file
57
vendor/k8s.io/apiserver/pkg/server/routes/version.go
generated
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
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 routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/emicklei/go-restful"
|
||||
|
||||
"k8s.io/apimachinery/pkg/version"
|
||||
"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
|
||||
)
|
||||
|
||||
// Version provides a webservice with version information.
|
||||
type Version struct {
|
||||
Version *version.Info
|
||||
}
|
||||
|
||||
// Install registers the APIServer's `/version` handler.
|
||||
func (v Version) Install(c *restful.Container) {
|
||||
if v.Version == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Set up a service to return the git code version.
|
||||
versionWS := new(restful.WebService)
|
||||
versionWS.Path("/version")
|
||||
versionWS.Doc("git code version from which this is built")
|
||||
versionWS.Route(
|
||||
versionWS.GET("/").To(v.handleVersion).
|
||||
Doc("get the code version").
|
||||
Operation("getCodeVersion").
|
||||
Produces(restful.MIME_JSON).
|
||||
Consumes(restful.MIME_JSON).
|
||||
Writes(version.Info{}))
|
||||
|
||||
c.Add(versionWS)
|
||||
}
|
||||
|
||||
// handleVersion writes the server's version information.
|
||||
func (v Version) handleVersion(req *restful.Request, resp *restful.Response) {
|
||||
responsewriters.WriteRawJSON(http.StatusOK, *v.Version, resp.ResponseWriter)
|
||||
}
|
||||
212
vendor/k8s.io/apiserver/pkg/server/serve.go
generated
vendored
Normal file
212
vendor/k8s.io/apiserver/pkg/server/serve.go
generated
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
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"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultKeepAlivePeriod = 3 * time.Minute
|
||||
)
|
||||
|
||||
// serveSecurely 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. serveSecurely does not block.
|
||||
func (s *GenericAPIServer) serveSecurely(stopCh <-chan struct{}) error {
|
||||
secureServer := &http.Server{
|
||||
Addr: s.SecureServingInfo.BindAddress,
|
||||
Handler: s.Handler,
|
||||
MaxHeaderBytes: 1 << 20,
|
||||
TLSConfig: &tls.Config{
|
||||
NameToCertificate: s.SecureServingInfo.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.SecureServingInfo.MinTLSVersion > 0 {
|
||||
secureServer.TLSConfig.MinVersion = s.SecureServingInfo.MinTLSVersion
|
||||
}
|
||||
if len(s.SecureServingInfo.CipherSuites) > 0 {
|
||||
secureServer.TLSConfig.CipherSuites = s.SecureServingInfo.CipherSuites
|
||||
}
|
||||
|
||||
if s.SecureServingInfo.Cert != nil {
|
||||
secureServer.TLSConfig.Certificates = []tls.Certificate{*s.SecureServingInfo.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.SecureServingInfo.SNICerts {
|
||||
secureServer.TLSConfig.Certificates = append(secureServer.TLSConfig.Certificates, *c)
|
||||
}
|
||||
|
||||
if s.SecureServingInfo.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.SecureServingInfo.ClientCA
|
||||
}
|
||||
|
||||
glog.Infof("Serving securely on %s", s.SecureServingInfo.BindAddress)
|
||||
var err error
|
||||
s.effectiveSecurePort, err = RunServer(secureServer, s.SecureServingInfo.BindNetwork, stopCh)
|
||||
return err
|
||||
}
|
||||
|
||||
// RunServer listens on the given port, then spawns a go-routine continuously serving
|
||||
// until the stopCh is closed. The port is returned. This function does not block.
|
||||
func RunServer(server *http.Server, network string, stopCh <-chan struct{}) (int, error) {
|
||||
if len(server.Addr) == 0 {
|
||||
return 0, errors.New("address cannot be empty")
|
||||
}
|
||||
|
||||
if len(network) == 0 {
|
||||
network = "tcp"
|
||||
}
|
||||
|
||||
ln, err := net.Listen(network, server.Addr)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to listen on %v: %v", server.Addr, err)
|
||||
}
|
||||
|
||||
// get port
|
||||
tcpAddr, ok := ln.Addr().(*net.TCPAddr)
|
||||
if !ok {
|
||||
ln.Close()
|
||||
return 0, fmt.Errorf("invalid listen address: %q", ln.Addr().String())
|
||||
}
|
||||
|
||||
// Stop the server by closing the listener
|
||||
go func() {
|
||||
<-stopCh
|
||||
ln.Close()
|
||||
}()
|
||||
|
||||
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", tcpAddr.String())
|
||||
select {
|
||||
case <-stopCh:
|
||||
glog.Info(msg)
|
||||
default:
|
||||
panic(fmt.Sprintf("%s due to error: %v", msg, err))
|
||||
}
|
||||
}()
|
||||
|
||||
return tcpAddr.Port, 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 is
|
||||
// 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
|
||||
}
|
||||
43
vendor/k8s.io/apiserver/pkg/server/signal.go
generated
vendored
Normal file
43
vendor/k8s.io/apiserver/pkg/server/signal.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
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{})
|
||||
|
||||
// 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() (stopCh <-chan struct{}) {
|
||||
close(onlyOneSignalHandler) // panics when called twice
|
||||
|
||||
stop := make(chan struct{})
|
||||
c := make(chan os.Signal, 2)
|
||||
signal.Notify(c, shutdownSignals...)
|
||||
go func() {
|
||||
<-c
|
||||
close(stop)
|
||||
<-c
|
||||
os.Exit(1) // second signal. Exit directly.
|
||||
}()
|
||||
|
||||
return stop
|
||||
}
|
||||
26
vendor/k8s.io/apiserver/pkg/server/signal_posix.go
generated
vendored
Normal file
26
vendor/k8s.io/apiserver/pkg/server/signal_posix.go
generated
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// +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
Normal file
23
vendor/k8s.io/apiserver/pkg/server/signal_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
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}
|
||||
64
vendor/k8s.io/apiserver/pkg/server/storage/BUILD
generated
vendored
Normal file
64
vendor/k8s.io/apiserver/pkg/server/storage/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"resource_config_test.go",
|
||||
"storage_factory_test.go",
|
||||
],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery/announced:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/example:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/example/install:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"doc.go",
|
||||
"resource_config.go",
|
||||
"resource_encoding_config.go",
|
||||
"storage_codec.go",
|
||||
"storage_factory.go",
|
||||
],
|
||||
deps = [
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apimachinery/registered:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/recognizer:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/features:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/value:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
18
vendor/k8s.io/apiserver/pkg/server/storage/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/apiserver/pkg/server/storage/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
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 storage contains the plumbing to setup the etcd storage of the apiserver.
|
||||
package storage
|
||||
185
vendor/k8s.io/apiserver/pkg/server/storage/resource_config.go
generated
vendored
Normal file
185
vendor/k8s.io/apiserver/pkg/server/storage/resource_config.go
generated
vendored
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
)
|
||||
|
||||
// APIResourceConfigSource is the interface to determine which versions and resources are enabled
|
||||
type APIResourceConfigSource interface {
|
||||
AnyVersionOfResourceEnabled(resource schema.GroupResource) bool
|
||||
ResourceEnabled(resource schema.GroupVersionResource) bool
|
||||
AllResourcesForVersionEnabled(version schema.GroupVersion) bool
|
||||
AnyResourcesForVersionEnabled(version schema.GroupVersion) bool
|
||||
AnyResourcesForGroupEnabled(group string) bool
|
||||
}
|
||||
|
||||
// Specifies the overrides for various API group versions.
|
||||
// This can be used to enable/disable entire group versions or specific resources.
|
||||
type GroupVersionResourceConfig struct {
|
||||
// Whether to enable or disable this entire group version. This dominates any enablement check.
|
||||
// Enable=true means the group version is enabled, and EnabledResources/DisabledResources are considered.
|
||||
// Enable=false means the group version is disabled, and EnabledResources/DisabledResources are not considered.
|
||||
Enable bool
|
||||
|
||||
// DisabledResources lists the resources that are specifically disabled for a group/version
|
||||
// DisabledResources trumps EnabledResources
|
||||
DisabledResources sets.String
|
||||
|
||||
// EnabledResources lists the resources that should be enabled by default. This is a little
|
||||
// unusual, but we need it for compatibility with old code for now. An empty set means
|
||||
// enable all, a non-empty set means that all other resources are disabled.
|
||||
EnabledResources sets.String
|
||||
}
|
||||
|
||||
var _ APIResourceConfigSource = &ResourceConfig{}
|
||||
|
||||
type ResourceConfig struct {
|
||||
GroupVersionResourceConfigs map[schema.GroupVersion]*GroupVersionResourceConfig
|
||||
}
|
||||
|
||||
func NewResourceConfig() *ResourceConfig {
|
||||
return &ResourceConfig{GroupVersionResourceConfigs: map[schema.GroupVersion]*GroupVersionResourceConfig{}}
|
||||
}
|
||||
|
||||
func NewGroupVersionResourceConfig() *GroupVersionResourceConfig {
|
||||
return &GroupVersionResourceConfig{Enable: true, DisabledResources: sets.String{}, EnabledResources: sets.String{}}
|
||||
}
|
||||
|
||||
// DisableVersions disables the versions entirely. No resources (even those whitelisted in EnabledResources) will be enabled
|
||||
func (o *ResourceConfig) DisableVersions(versions ...schema.GroupVersion) {
|
||||
for _, version := range versions {
|
||||
_, versionExists := o.GroupVersionResourceConfigs[version]
|
||||
if !versionExists {
|
||||
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
|
||||
}
|
||||
|
||||
o.GroupVersionResourceConfigs[version].Enable = false
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ResourceConfig) EnableVersions(versions ...schema.GroupVersion) {
|
||||
for _, version := range versions {
|
||||
_, versionExists := o.GroupVersionResourceConfigs[version]
|
||||
if !versionExists {
|
||||
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
|
||||
}
|
||||
|
||||
o.GroupVersionResourceConfigs[version].Enable = true
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ResourceConfig) DisableResources(resources ...schema.GroupVersionResource) {
|
||||
for _, resource := range resources {
|
||||
version := resource.GroupVersion()
|
||||
_, versionExists := o.GroupVersionResourceConfigs[version]
|
||||
if !versionExists {
|
||||
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
|
||||
}
|
||||
|
||||
o.GroupVersionResourceConfigs[version].DisabledResources.Insert(resource.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
func (o *ResourceConfig) EnableResources(resources ...schema.GroupVersionResource) {
|
||||
for _, resource := range resources {
|
||||
version := resource.GroupVersion()
|
||||
_, versionExists := o.GroupVersionResourceConfigs[version]
|
||||
if !versionExists {
|
||||
o.GroupVersionResourceConfigs[version] = NewGroupVersionResourceConfig()
|
||||
}
|
||||
|
||||
o.GroupVersionResourceConfigs[version].EnabledResources.Insert(resource.Resource)
|
||||
o.GroupVersionResourceConfigs[version].DisabledResources.Delete(resource.Resource)
|
||||
}
|
||||
}
|
||||
|
||||
// AnyResourcesForVersionEnabled only considers matches based on exactly group/resource lexical matching. This means that
|
||||
// resource renames across versions are NOT considered to be the same resource by this method. You'll need to manually check
|
||||
// using the ResourceEnabled function.
|
||||
func (o *ResourceConfig) AnyVersionOfResourceEnabled(resource schema.GroupResource) bool {
|
||||
for version := range o.GroupVersionResourceConfigs {
|
||||
if version.Group != resource.Group {
|
||||
continue
|
||||
}
|
||||
|
||||
if o.ResourceEnabled(version.WithResource(resource.Resource)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (o *ResourceConfig) ResourceEnabled(resource schema.GroupVersionResource) bool {
|
||||
versionOverride, versionExists := o.GroupVersionResourceConfigs[resource.GroupVersion()]
|
||||
if !versionExists {
|
||||
return false
|
||||
}
|
||||
if !versionOverride.Enable {
|
||||
return false
|
||||
}
|
||||
|
||||
if versionOverride.DisabledResources.Has(resource.Resource) {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(versionOverride.EnabledResources) > 0 {
|
||||
return versionOverride.EnabledResources.Has(resource.Resource)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (o *ResourceConfig) AllResourcesForVersionEnabled(version schema.GroupVersion) bool {
|
||||
versionOverride, versionExists := o.GroupVersionResourceConfigs[version]
|
||||
if !versionExists {
|
||||
return false
|
||||
}
|
||||
if !versionOverride.Enable {
|
||||
return false
|
||||
}
|
||||
|
||||
if len(versionOverride.EnabledResources) == 0 && len(versionOverride.DisabledResources) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (o *ResourceConfig) AnyResourcesForVersionEnabled(version schema.GroupVersion) bool {
|
||||
versionOverride, versionExists := o.GroupVersionResourceConfigs[version]
|
||||
if !versionExists {
|
||||
return false
|
||||
}
|
||||
|
||||
return versionOverride.Enable
|
||||
}
|
||||
|
||||
func (o *ResourceConfig) AnyResourcesForGroupEnabled(group string) bool {
|
||||
for version := range o.GroupVersionResourceConfigs {
|
||||
if version.Group == group {
|
||||
if o.AnyResourcesForVersionEnabled(version) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
160
vendor/k8s.io/apiserver/pkg/server/storage/resource_config_test.go
generated
vendored
Normal file
160
vendor/k8s.io/apiserver/pkg/server/storage/resource_config_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
func TestDisabledVersion(t *testing.T) {
|
||||
g1v1 := schema.GroupVersion{Group: "group1", Version: "version1"}
|
||||
g1v2 := schema.GroupVersion{Group: "group1", Version: "version2"}
|
||||
g2v1 := schema.GroupVersion{Group: "group2", Version: "version1"}
|
||||
g3v1 := schema.GroupVersion{Group: "group3", Version: "version1"}
|
||||
|
||||
resourceType := "the-resource"
|
||||
disabledResourceType := "the-disabled-resource"
|
||||
|
||||
config := NewResourceConfig()
|
||||
|
||||
config.DisableVersions(g1v1)
|
||||
config.EnableVersions(g1v2, g3v1)
|
||||
config.EnableResources(g1v1.WithResource(resourceType), g2v1.WithResource(resourceType))
|
||||
config.DisableResources(g1v2.WithResource(disabledResourceType))
|
||||
|
||||
expectedEnabledResources := []schema.GroupVersionResource{
|
||||
g1v2.WithResource(resourceType),
|
||||
g2v1.WithResource(resourceType),
|
||||
}
|
||||
expectedDisabledResources := []schema.GroupVersionResource{
|
||||
g1v1.WithResource(resourceType), g1v1.WithResource(disabledResourceType),
|
||||
g1v2.WithResource(disabledResourceType),
|
||||
g2v1.WithResource(disabledResourceType),
|
||||
}
|
||||
|
||||
for _, expectedResource := range expectedEnabledResources {
|
||||
if !config.ResourceEnabled(expectedResource) {
|
||||
t.Errorf("expected enabled for %v, from %v", expectedResource, config)
|
||||
}
|
||||
}
|
||||
for _, expectedResource := range expectedDisabledResources {
|
||||
if config.ResourceEnabled(expectedResource) {
|
||||
t.Errorf("expected disabled for %v, from %v", expectedResource, config)
|
||||
}
|
||||
}
|
||||
|
||||
if e, a := false, config.AnyResourcesForVersionEnabled(g1v1); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := false, config.AllResourcesForVersionEnabled(g1v1); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := true, config.AnyResourcesForVersionEnabled(g1v2); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := false, config.AllResourcesForVersionEnabled(g1v2); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := true, config.AnyResourcesForVersionEnabled(g3v1); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := true, config.AllResourcesForVersionEnabled(g3v1); e != a {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
expectedEnabledAnyVersionResources := []schema.GroupResource{
|
||||
{Group: "group1", Resource: resourceType},
|
||||
}
|
||||
expectedDisabledAnyResources := []schema.GroupResource{
|
||||
{Group: "group1", Resource: disabledResourceType},
|
||||
}
|
||||
|
||||
for _, expectedResource := range expectedEnabledAnyVersionResources {
|
||||
if !config.AnyVersionOfResourceEnabled(expectedResource) {
|
||||
t.Errorf("expected enabled for %v, from %v", expectedResource, config)
|
||||
}
|
||||
}
|
||||
for _, expectedResource := range expectedDisabledAnyResources {
|
||||
if config.AnyVersionOfResourceEnabled(expectedResource) {
|
||||
t.Errorf("expected disabled for %v, from %v", expectedResource, config)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAnyResourcesForGroupEnabled(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
creator func() APIResourceConfigSource
|
||||
testGroup string
|
||||
|
||||
expectedResult bool
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
creator: func() APIResourceConfigSource {
|
||||
return NewResourceConfig()
|
||||
},
|
||||
testGroup: "one",
|
||||
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "present, but disabled",
|
||||
creator: func() APIResourceConfigSource {
|
||||
ret := NewResourceConfig()
|
||||
ret.DisableVersions(schema.GroupVersion{Group: "one", Version: "version1"})
|
||||
return ret
|
||||
},
|
||||
testGroup: "one",
|
||||
|
||||
expectedResult: false,
|
||||
},
|
||||
{
|
||||
name: "present, and one version enabled",
|
||||
creator: func() APIResourceConfigSource {
|
||||
ret := NewResourceConfig()
|
||||
ret.DisableVersions(schema.GroupVersion{Group: "one", Version: "version1"})
|
||||
ret.EnableVersions(schema.GroupVersion{Group: "one", Version: "version2"})
|
||||
return ret
|
||||
},
|
||||
testGroup: "one",
|
||||
|
||||
expectedResult: true,
|
||||
},
|
||||
{
|
||||
name: "present, and one resource",
|
||||
creator: func() APIResourceConfigSource {
|
||||
ret := NewResourceConfig()
|
||||
ret.DisableVersions(schema.GroupVersion{Group: "one", Version: "version1"})
|
||||
ret.EnableResources(schema.GroupVersionResource{Group: "one", Version: "version2", Resource: "foo"})
|
||||
return ret
|
||||
},
|
||||
testGroup: "one",
|
||||
|
||||
expectedResult: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
if e, a := tc.expectedResult, tc.creator().AnyResourcesForGroupEnabled(tc.testGroup); e != a {
|
||||
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
|
||||
}
|
||||
}
|
||||
}
|
||||
119
vendor/k8s.io/apiserver/pkg/server/storage/resource_encoding_config.go
generated
vendored
Normal file
119
vendor/k8s.io/apiserver/pkg/server/storage/resource_encoding_config.go
generated
vendored
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/apimachinery/registered"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
type ResourceEncodingConfig interface {
|
||||
// StorageEncoding returns the serialization format for the resource.
|
||||
// TODO this should actually return a GroupVersionKind since you can logically have multiple "matching" Kinds
|
||||
// For now, it returns just the GroupVersion for consistency with old behavior
|
||||
StorageEncodingFor(schema.GroupResource) (schema.GroupVersion, error)
|
||||
|
||||
// InMemoryEncodingFor returns the groupVersion for the in memory representation the storage should convert to.
|
||||
InMemoryEncodingFor(schema.GroupResource) (schema.GroupVersion, error)
|
||||
}
|
||||
|
||||
type DefaultResourceEncodingConfig struct {
|
||||
groups map[string]*GroupResourceEncodingConfig
|
||||
registry *registered.APIRegistrationManager
|
||||
}
|
||||
|
||||
type GroupResourceEncodingConfig struct {
|
||||
DefaultExternalEncoding schema.GroupVersion
|
||||
ExternalResourceEncodings map[string]schema.GroupVersion
|
||||
|
||||
DefaultInternalEncoding schema.GroupVersion
|
||||
InternalResourceEncodings map[string]schema.GroupVersion
|
||||
}
|
||||
|
||||
var _ ResourceEncodingConfig = &DefaultResourceEncodingConfig{}
|
||||
|
||||
func NewDefaultResourceEncodingConfig(registry *registered.APIRegistrationManager) *DefaultResourceEncodingConfig {
|
||||
return &DefaultResourceEncodingConfig{groups: map[string]*GroupResourceEncodingConfig{}, registry: registry}
|
||||
}
|
||||
|
||||
func newGroupResourceEncodingConfig(defaultEncoding, defaultInternalVersion schema.GroupVersion) *GroupResourceEncodingConfig {
|
||||
return &GroupResourceEncodingConfig{
|
||||
DefaultExternalEncoding: defaultEncoding, ExternalResourceEncodings: map[string]schema.GroupVersion{},
|
||||
DefaultInternalEncoding: defaultInternalVersion, InternalResourceEncodings: map[string]schema.GroupVersion{},
|
||||
}
|
||||
}
|
||||
|
||||
func (o *DefaultResourceEncodingConfig) SetVersionEncoding(group string, externalEncodingVersion, internalVersion schema.GroupVersion) {
|
||||
_, groupExists := o.groups[group]
|
||||
if !groupExists {
|
||||
o.groups[group] = newGroupResourceEncodingConfig(externalEncodingVersion, internalVersion)
|
||||
}
|
||||
|
||||
o.groups[group].DefaultExternalEncoding = externalEncodingVersion
|
||||
o.groups[group].DefaultInternalEncoding = internalVersion
|
||||
}
|
||||
|
||||
func (o *DefaultResourceEncodingConfig) SetResourceEncoding(resourceBeingStored schema.GroupResource, externalEncodingVersion, internalVersion schema.GroupVersion) {
|
||||
group := resourceBeingStored.Group
|
||||
_, groupExists := o.groups[group]
|
||||
if !groupExists {
|
||||
o.groups[group] = newGroupResourceEncodingConfig(externalEncodingVersion, internalVersion)
|
||||
}
|
||||
|
||||
o.groups[group].ExternalResourceEncodings[resourceBeingStored.Resource] = externalEncodingVersion
|
||||
o.groups[group].InternalResourceEncodings[resourceBeingStored.Resource] = internalVersion
|
||||
}
|
||||
|
||||
func (o *DefaultResourceEncodingConfig) StorageEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
|
||||
groupMeta, err := o.registry.Group(resource.Group)
|
||||
if err != nil {
|
||||
return schema.GroupVersion{}, err
|
||||
}
|
||||
|
||||
groupEncoding, groupExists := o.groups[resource.Group]
|
||||
|
||||
if !groupExists {
|
||||
// return the most preferred external version for the group
|
||||
return groupMeta.GroupVersion, nil
|
||||
}
|
||||
|
||||
resourceOverride, resourceExists := groupEncoding.ExternalResourceEncodings[resource.Resource]
|
||||
if !resourceExists {
|
||||
return groupEncoding.DefaultExternalEncoding, nil
|
||||
}
|
||||
|
||||
return resourceOverride, nil
|
||||
}
|
||||
|
||||
func (o *DefaultResourceEncodingConfig) InMemoryEncodingFor(resource schema.GroupResource) (schema.GroupVersion, error) {
|
||||
if _, err := o.registry.Group(resource.Group); err != nil {
|
||||
return schema.GroupVersion{}, err
|
||||
}
|
||||
|
||||
groupEncoding, groupExists := o.groups[resource.Group]
|
||||
if !groupExists {
|
||||
return schema.GroupVersion{Group: resource.Group, Version: runtime.APIVersionInternal}, nil
|
||||
}
|
||||
|
||||
resourceOverride, resourceExists := groupEncoding.InternalResourceEncodings[resource.Resource]
|
||||
if !resourceExists {
|
||||
return groupEncoding.DefaultInternalEncoding, nil
|
||||
}
|
||||
|
||||
return resourceOverride, nil
|
||||
}
|
||||
108
vendor/k8s.io/apiserver/pkg/server/storage/storage_codec.go
generated
vendored
Normal file
108
vendor/k8s.io/apiserver/pkg/server/storage/storage_codec.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
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 storage
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mime"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
// StorageCodecConfig are the arguments passed to newStorageCodecFn
|
||||
type StorageCodecConfig struct {
|
||||
StorageMediaType string
|
||||
StorageSerializer runtime.StorageSerializer
|
||||
StorageVersion schema.GroupVersion
|
||||
MemoryVersion schema.GroupVersion
|
||||
Config storagebackend.Config
|
||||
|
||||
EncoderDecoratorFn func(runtime.Encoder) runtime.Encoder
|
||||
DecoderDecoratorFn func([]runtime.Decoder) []runtime.Decoder
|
||||
}
|
||||
|
||||
// NewStorageCodec assembles a storage codec for the provided storage media type, the provided serializer, and the requested
|
||||
// storage and memory versions.
|
||||
func NewStorageCodec(opts StorageCodecConfig) (runtime.Codec, error) {
|
||||
mediaType, _, err := mime.ParseMediaType(opts.StorageMediaType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%q is not a valid mime-type", opts.StorageMediaType)
|
||||
}
|
||||
|
||||
if opts.Config.Type == storagebackend.StorageTypeETCD2 && mediaType != "application/json" {
|
||||
glog.Warningf(`storage type %q does not support media type %q, using "application/json"`, storagebackend.StorageTypeETCD2, mediaType)
|
||||
mediaType = "application/json"
|
||||
}
|
||||
|
||||
serializer, ok := runtime.SerializerInfoForMediaType(opts.StorageSerializer.SupportedMediaTypes(), mediaType)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unable to find serializer for %q", mediaType)
|
||||
}
|
||||
|
||||
s := serializer.Serializer
|
||||
|
||||
// make sure the selected encoder supports string data
|
||||
if !serializer.EncodesAsText && opts.Config.Type == storagebackend.StorageTypeETCD2 {
|
||||
return nil, fmt.Errorf("storage type %q does not support binary media type %q", storagebackend.StorageTypeETCD2, mediaType)
|
||||
}
|
||||
|
||||
// Give callers the opportunity to wrap encoders and decoders. For decoders, each returned decoder will
|
||||
// be passed to the recognizer so that multiple decoders are available.
|
||||
var encoder runtime.Encoder = s
|
||||
if opts.EncoderDecoratorFn != nil {
|
||||
encoder = opts.EncoderDecoratorFn(encoder)
|
||||
}
|
||||
decoders := []runtime.Decoder{
|
||||
// selected decoder as the primary
|
||||
s,
|
||||
// universal deserializer as a fallback
|
||||
opts.StorageSerializer.UniversalDeserializer(),
|
||||
// base64-wrapped universal deserializer as a last resort.
|
||||
// this allows reading base64-encoded protobuf, which should only exist if etcd2+protobuf was used at some point.
|
||||
// data written that way could exist in etcd2, or could have been migrated to etcd3.
|
||||
// TODO: flag this type of data if we encounter it, require migration (read to decode, write to persist using a supported encoder), and remove in 1.8
|
||||
runtime.NewBase64Serializer(nil, opts.StorageSerializer.UniversalDeserializer()),
|
||||
}
|
||||
if opts.DecoderDecoratorFn != nil {
|
||||
decoders = opts.DecoderDecoratorFn(decoders)
|
||||
}
|
||||
|
||||
// Ensure the storage receives the correct version.
|
||||
encoder = opts.StorageSerializer.EncoderForVersion(
|
||||
encoder,
|
||||
runtime.NewMultiGroupVersioner(
|
||||
opts.StorageVersion,
|
||||
schema.GroupKind{Group: opts.StorageVersion.Group},
|
||||
schema.GroupKind{Group: opts.MemoryVersion.Group},
|
||||
),
|
||||
)
|
||||
decoder := opts.StorageSerializer.DecoderToVersion(
|
||||
recognizer.NewDecoder(decoders...),
|
||||
runtime.NewMultiGroupVersioner(
|
||||
opts.MemoryVersion,
|
||||
schema.GroupKind{Group: opts.MemoryVersion.Group},
|
||||
schema.GroupKind{Group: opts.StorageVersion.Group},
|
||||
),
|
||||
)
|
||||
|
||||
return runtime.NewCodec(encoder, decoder), nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue