Commit 42973b05 authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #40947 from deads2k/apiserver-04-invert

Automatic merge from submit-queue (batch tested with PRs 41023, 41031, 40947) apiserver command line options lead to config Logically command line options lead to config, not the other way around. We're clean enough now we can actually do the inversion. WIP because I have some test cycles to fix, but this is all the meat. @kubernetes/sig-api-machinery-misc
parents 98a16d69 250408ee
...@@ -112,13 +112,14 @@ func (o AggregatorOptions) RunAggregator() error { ...@@ -112,13 +112,14 @@ func (o AggregatorOptions) RunAggregator() error {
genericAPIServerConfig := genericapiserver.NewConfig(). genericAPIServerConfig := genericapiserver.NewConfig().
WithSerializer(api.Codecs) WithSerializer(api.Codecs)
if _, err := genericAPIServerConfig.ApplySecureServingOptions(o.SecureServing); err != nil {
return err if err := o.SecureServing.ApplyTo(genericAPIServerConfig); err != nil {
return fmt.Errorf("failed to configure https: %s", err)
} }
if _, err := genericAPIServerConfig.ApplyDelegatingAuthenticationOptions(o.Authentication); err != nil { if err := o.Authentication.ApplyTo(genericAPIServerConfig); err != nil {
return err return err
} }
if _, err := genericAPIServerConfig.ApplyDelegatingAuthorizationOptions(o.Authorization); err != nil { if err := o.Authorization.ApplyTo(genericAPIServerConfig); err != nil {
return err return err
} }
genericAPIServerConfig.LongRunningFunc = filters.BasicLongRunningRequestCheck( genericAPIServerConfig.LongRunningFunc = filters.BasicLongRunningRequestCheck(
......
...@@ -106,15 +106,19 @@ func Run(s *options.ServerRunOptions) error { ...@@ -106,15 +106,19 @@ func Run(s *options.ServerRunOptions) error {
// create config from options // create config from options
genericConfig := genericapiserver.NewConfig(). genericConfig := genericapiserver.NewConfig().
WithSerializer(api.Codecs). WithSerializer(api.Codecs)
ApplyOptions(s.GenericServerRunOptions).
ApplyInsecureServingOptions(s.InsecureServing)
if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { if err := s.GenericServerRunOptions.ApplyTo(genericConfig); err != nil {
return fmt.Errorf("failed to configure https: %s", err) return err
}
if err := s.InsecureServing.ApplyTo(genericConfig); err != nil {
return err
} }
if err = s.Authentication.Apply(genericConfig); err != nil { if err := s.SecureServing.ApplyTo(genericConfig); err != nil {
return fmt.Errorf("failed to configure authentication: %s", err) return err
}
if err := s.Authentication.ApplyTo(genericConfig); err != nil {
return err
} }
capabilities.Initialize(capabilities.Capabilities{ capabilities.Initialize(capabilities.Capabilities{
......
...@@ -104,14 +104,18 @@ func (serverOptions *ServerRunOptions) Run(stopCh <-chan struct{}) error { ...@@ -104,14 +104,18 @@ func (serverOptions *ServerRunOptions) Run(stopCh <-chan struct{}) error {
// create config from options // create config from options
config := genericapiserver.NewConfig(). config := genericapiserver.NewConfig().
WithSerializer(api.Codecs). WithSerializer(api.Codecs)
ApplyOptions(serverOptions.GenericServerRunOptions).
ApplyInsecureServingOptions(serverOptions.InsecureServing)
if _, err := config.ApplySecureServingOptions(serverOptions.SecureServing); err != nil { if err := serverOptions.GenericServerRunOptions.ApplyTo(config); err != nil {
return err
}
if err := serverOptions.InsecureServing.ApplyTo(config); err != nil {
return err
}
if err := serverOptions.SecureServing.ApplyTo(config); err != nil {
return fmt.Errorf("failed to configure https: %s", err) return fmt.Errorf("failed to configure https: %s", err)
} }
if err := serverOptions.Authentication.Apply(config); err != nil { if err := serverOptions.Authentication.ApplyTo(config); err != nil {
return fmt.Errorf("failed to configure authentication: %s", err) return fmt.Errorf("failed to configure authentication: %s", err)
} }
......
...@@ -89,15 +89,19 @@ func Run(s *options.ServerRunOptions) error { ...@@ -89,15 +89,19 @@ func Run(s *options.ServerRunOptions) error {
} }
genericConfig := genericapiserver.NewConfig(). genericConfig := genericapiserver.NewConfig().
WithSerializer(api.Codecs). WithSerializer(api.Codecs)
ApplyOptions(s.GenericServerRunOptions).
ApplyInsecureServingOptions(s.InsecureServing)
if _, err := genericConfig.ApplySecureServingOptions(s.SecureServing); err != nil { if err := s.GenericServerRunOptions.ApplyTo(genericConfig); err != nil {
return fmt.Errorf("failed to configure https: %s", err) return err
}
if err := s.InsecureServing.ApplyTo(genericConfig); err != nil {
return err
} }
if err := s.Authentication.Apply(genericConfig); err != nil { if err := s.SecureServing.ApplyTo(genericConfig); err != nil {
return fmt.Errorf("failed to configure authentication: %s", err) return err
}
if err := s.Authentication.ApplyTo(genericConfig); err != nil {
return err
} }
// TODO: register cluster federation resources here. // TODO: register cluster federation resources here.
......
...@@ -297,7 +297,7 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.Au ...@@ -297,7 +297,7 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.Au
return ret return ret
} }
func (o *BuiltInAuthenticationOptions) Apply(c *genericapiserver.Config) error { func (o *BuiltInAuthenticationOptions) ApplyTo(c *genericapiserver.Config) error {
if o == nil { if o == nil {
return nil return nil
} }
......
...@@ -19,10 +19,8 @@ package server ...@@ -19,10 +19,8 @@ package server
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/pem"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"net" "net"
"net/http" "net/http"
goruntime "runtime" goruntime "runtime"
...@@ -34,7 +32,6 @@ import ( ...@@ -34,7 +32,6 @@ import (
"github.com/emicklei/go-restful/swagger" "github.com/emicklei/go-restful/swagger"
"github.com/go-openapi/spec" "github.com/go-openapi/spec"
"github.com/pborman/uuid" "github.com/pborman/uuid"
"gopkg.in/natefinch/lumberjack.v2"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
openapicommon "k8s.io/apimachinery/pkg/openapi" openapicommon "k8s.io/apimachinery/pkg/openapi"
...@@ -56,7 +53,6 @@ import ( ...@@ -56,7 +53,6 @@ import (
genericfilters "k8s.io/apiserver/pkg/server/filters" genericfilters "k8s.io/apiserver/pkg/server/filters"
"k8s.io/apiserver/pkg/server/healthz" "k8s.io/apiserver/pkg/server/healthz"
"k8s.io/apiserver/pkg/server/mux" "k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/options"
"k8s.io/apiserver/pkg/server/routes" "k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
...@@ -193,25 +189,23 @@ type SecureServingInfo struct { ...@@ -193,25 +189,23 @@ type SecureServingInfo struct {
// NewConfig returns a Config struct with the default values // NewConfig returns a Config struct with the default values
func NewConfig() *Config { func NewConfig() *Config {
config := &Config{ return &Config{
ReadWritePort: 6443, ReadWritePort: 6443,
RequestContextMapper: apirequest.NewRequestContextMapper(), RequestContextMapper: apirequest.NewRequestContextMapper(),
BuildHandlerChainsFunc: DefaultBuildHandlerChain, BuildHandlerChainsFunc: DefaultBuildHandlerChain,
LegacyAPIGroupPrefixes: sets.NewString(DefaultLegacyAPIPrefix), LegacyAPIGroupPrefixes: sets.NewString(DefaultLegacyAPIPrefix),
HealthzChecks: []healthz.HealthzChecker{healthz.PingHealthz}, HealthzChecks: []healthz.HealthzChecker{healthz.PingHealthz},
EnableIndex: true, EnableIndex: true,
EnableGarbageCollection: true,
EnableProfiling: true,
MaxRequestsInFlight: 400,
MaxMutatingRequestsInFlight: 200,
MinRequestTimeout: 1800,
// Default to treating watch as a long-running operation // Default to treating watch as a long-running operation
// Generic API servers have no inherent long-running subresources // Generic API servers have no inherent long-running subresources
LongRunningFunc: genericfilters.BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString()), LongRunningFunc: genericfilters.BasicLongRunningRequestCheck(sets.NewString("watch"), sets.NewString()),
} }
// this keeps the defaults in sync
defaultOptions := options.NewServerRunOptions()
// unset fields that can be overridden to avoid setting values so that we won't end up with lingering values.
// TODO we probably want to run the defaults the other way. A default here drives it in the CLI flags
defaultOptions.AuditLogPath = ""
return config.ApplyOptions(defaultOptions)
} }
func (c *Config) WithSerializer(codecs serializer.CodecFactory) *Config { func (c *Config) WithSerializer(codecs serializer.CodecFactory) *Config {
...@@ -257,82 +251,6 @@ func DefaultSwaggerConfig() *swagger.Config { ...@@ -257,82 +251,6 @@ func DefaultSwaggerConfig() *swagger.Config {
} }
} }
func (c *Config) ApplySecureServingOptions(secureServing *options.SecureServingOptions) (*Config, error) {
if secureServing == nil || secureServing.ServingOptions.BindPort <= 0 {
return c, nil
}
secureServingInfo := &SecureServingInfo{
ServingInfo: ServingInfo{
BindAddress: net.JoinHostPort(secureServing.ServingOptions.BindAddress.String(), strconv.Itoa(secureServing.ServingOptions.BindPort)),
},
}
serverCertFile, serverKeyFile := secureServing.ServerCert.CertKey.CertFile, secureServing.ServerCert.CertKey.KeyFile
// load main cert
if len(serverCertFile) != 0 || len(serverKeyFile) != 0 {
tlsCert, err := tls.LoadX509KeyPair(serverCertFile, serverKeyFile)
if err != nil {
return nil, fmt.Errorf("unable to load server certificate: %v", err)
}
secureServingInfo.Cert = &tlsCert
}
// optionally load CA cert
if len(secureServing.ServerCert.CACertFile) != 0 {
pemData, err := ioutil.ReadFile(secureServing.ServerCert.CACertFile)
if err != nil {
return nil, fmt.Errorf("failed to read certificate authority from %q: %v", secureServing.ServerCert.CACertFile, err)
}
block, pemData := pem.Decode(pemData)
if block == nil {
return nil, fmt.Errorf("no certificate found in certificate authority file %q", secureServing.ServerCert.CACertFile)
}
if block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("expected CERTIFICATE block in certiticate authority file %q, found: %s", secureServing.ServerCert.CACertFile, block.Type)
}
secureServingInfo.CACert = &tls.Certificate{
Certificate: [][]byte{block.Bytes},
}
}
// load SNI certs
namedTlsCerts := make([]namedTlsCert, 0, len(secureServing.SNICertKeys))
for _, nck := range secureServing.SNICertKeys {
tlsCert, err := tls.LoadX509KeyPair(nck.CertFile, nck.KeyFile)
namedTlsCerts = append(namedTlsCerts, namedTlsCert{
tlsCert: tlsCert,
names: nck.Names,
})
if err != nil {
return nil, fmt.Errorf("failed to load SNI cert and key: %v", err)
}
}
var err error
secureServingInfo.SNICerts, err = getNamedCertificateMap(namedTlsCerts)
if err != nil {
return nil, err
}
c.SecureServingInfo = secureServingInfo
c.ReadWritePort = secureServing.ServingOptions.BindPort
return c, nil
}
func (c *Config) ApplyInsecureServingOptions(insecureServing *options.ServingOptions) *Config {
if insecureServing == nil || insecureServing.BindPort <= 0 {
return c
}
c.InsecureServingInfo = &ServingInfo{
BindAddress: net.JoinHostPort(insecureServing.BindAddress.String(), strconv.Itoa(insecureServing.BindPort)),
}
return c
}
func (c *Config) ApplyClientCert(clientCAFile string) (*Config, error) { func (c *Config) ApplyClientCert(clientCAFile string) (*Config, error) {
if c.SecureServingInfo != nil { if c.SecureServingInfo != nil {
if len(clientCAFile) > 0 { if len(clientCAFile) > 0 {
...@@ -352,83 +270,6 @@ func (c *Config) ApplyClientCert(clientCAFile string) (*Config, error) { ...@@ -352,83 +270,6 @@ func (c *Config) ApplyClientCert(clientCAFile string) (*Config, error) {
return c, nil return c, nil
} }
func (c *Config) ApplyDelegatingAuthenticationOptions(o *options.DelegatingAuthenticationOptions) (*Config, error) {
if o == nil {
return c, nil
}
var err error
c, err = c.ApplyClientCert(o.ClientCert.ClientCA)
if err != nil {
return nil, fmt.Errorf("unable to load client CA file: %v", err)
}
c, err = c.ApplyClientCert(o.RequestHeader.ClientCAFile)
if err != nil {
return nil, fmt.Errorf("unable to load client CA file: %v", err)
}
cfg, err := o.ToAuthenticationConfig()
if err != nil {
return nil, err
}
authenticator, securityDefinitions, err := cfg.New()
if err != nil {
return nil, err
}
c.Authenticator = authenticator
if c.OpenAPIConfig != nil {
c.OpenAPIConfig.SecurityDefinitions = securityDefinitions
}
c.SupportsBasicAuth = false
return c, nil
}
func (c *Config) ApplyDelegatingAuthorizationOptions(o *options.DelegatingAuthorizationOptions) (*Config, error) {
if o == nil {
return c, nil
}
cfg, err := o.ToAuthorizationConfig()
if err != nil {
return nil, err
}
authorizer, err := cfg.New()
if err != nil {
return nil, err
}
c.Authorizer = authorizer
return c, nil
}
// ApplyOptions applies the run options to the method receiver and returns self
func (c *Config) ApplyOptions(options *options.ServerRunOptions) *Config {
if len(options.AuditLogPath) != 0 {
c.AuditWriter = &lumberjack.Logger{
Filename: options.AuditLogPath,
MaxAge: options.AuditLogMaxAge,
MaxBackups: options.AuditLogMaxBackups,
MaxSize: options.AuditLogMaxSize,
}
}
c.CorsAllowedOriginList = options.CorsAllowedOriginList
c.EnableGarbageCollection = options.EnableGarbageCollection
c.EnableProfiling = options.EnableProfiling
c.EnableContentionProfiling = options.EnableContentionProfiling
c.EnableSwaggerUI = options.EnableSwaggerUI
c.ExternalAddress = options.ExternalHost
c.MaxRequestsInFlight = options.MaxRequestsInFlight
c.MaxMutatingRequestsInFlight = options.MaxMutatingRequestsInFlight
c.MinRequestTimeout = options.MinRequestTimeout
c.PublicAddress = options.AdvertiseAddress
return c
}
type completedConfig struct { type completedConfig struct {
*Config *Config
} }
......
...@@ -40,13 +40,13 @@ import ( ...@@ -40,13 +40,13 @@ import (
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/server/healthz"
restclient "k8s.io/client-go/rest"
genericapi "k8s.io/apiserver/pkg/endpoints" genericapi "k8s.io/apiserver/pkg/endpoints"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/registry/rest"
"k8s.io/apiserver/pkg/server/healthz"
genericmux "k8s.io/apiserver/pkg/server/mux" genericmux "k8s.io/apiserver/pkg/server/mux"
"k8s.io/apiserver/pkg/server/routes" "k8s.io/apiserver/pkg/server/routes"
restclient "k8s.io/client-go/rest"
) )
// Info about an API group. // Info about an API group.
...@@ -212,6 +212,11 @@ func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) { ...@@ -212,6 +212,11 @@ func (s preparedGenericAPIServer) Run(stopCh <-chan struct{}) {
<-stopCh <-stopCh
} }
// 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 // installAPIResources is a private method for installing the REST storage backing each api groupversionresource
func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo) error { func (s *GenericAPIServer) installAPIResources(apiPrefix string, apiGroupInfo *APIGroupInfo) error {
for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions { for _, groupVersion := range apiGroupInfo.GroupMeta.GroupVersions {
......
...@@ -17,11 +17,13 @@ limitations under the License. ...@@ -17,11 +17,13 @@ limitations under the License.
package options package options
import ( import (
"fmt"
"time" "time"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apiserver/pkg/authentication/authenticatorfactory" "k8s.io/apiserver/pkg/authentication/authenticatorfactory"
"k8s.io/apiserver/pkg/server"
authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1" authenticationclient "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
...@@ -128,6 +130,35 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) { ...@@ -128,6 +130,35 @@ func (s *DelegatingAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
s.RequestHeader.AddFlags(fs) s.RequestHeader.AddFlags(fs)
} }
func (s *DelegatingAuthenticationOptions) ApplyTo(c *server.Config) error {
var err error
c, err = c.ApplyClientCert(s.ClientCert.ClientCA)
if err != nil {
return fmt.Errorf("unable to load client CA file: %v", err)
}
c, err = c.ApplyClientCert(s.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) { func (s *DelegatingAuthenticationOptions) ToAuthenticationConfig() (authenticatorfactory.DelegatingAuthenticatorConfig, error) {
tokenClient, err := s.newTokenAccessReview() tokenClient, err := s.newTokenAccessReview()
if err != nil { if err != nil {
......
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
"k8s.io/apiserver/pkg/authorization/authorizerfactory" "k8s.io/apiserver/pkg/authorization/authorizerfactory"
"k8s.io/apiserver/pkg/server"
authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1beta1" authorizationclient "k8s.io/client-go/kubernetes/typed/authorization/v1beta1"
"k8s.io/client-go/rest" "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
...@@ -69,6 +70,20 @@ func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) { ...@@ -69,6 +70,20 @@ func (s *DelegatingAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
"The duration to cache 'unauthorized' responses from the webhook authorizer.") "The duration to cache 'unauthorized' responses from the webhook authorizer.")
} }
func (s *DelegatingAuthorizationOptions) ApplyTo(c *server.Config) error {
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) { func (s *DelegatingAuthorizationOptions) ToAuthorizationConfig() (authorizerfactory.DelegatingAuthorizerConfig, error) {
sarClient, err := s.newSubjectAccessReview() sarClient, err := s.newSubjectAccessReview()
if err != nil { if err != nil {
......
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission" "k8s.io/apiserver/pkg/admission"
"k8s.io/apiserver/pkg/server"
utilfeature "k8s.io/apiserver/pkg/util/feature" utilfeature "k8s.io/apiserver/pkg/util/feature"
utilflag "k8s.io/apiserver/pkg/util/flag" utilflag "k8s.io/apiserver/pkg/util/flag"
...@@ -30,6 +31,7 @@ import ( ...@@ -30,6 +31,7 @@ import (
_ "k8s.io/apiserver/pkg/features" _ "k8s.io/apiserver/pkg/features"
"github.com/spf13/pflag" "github.com/spf13/pflag"
"gopkg.in/natefinch/lumberjack.v2"
) )
// ServerRunOptions contains the options while running a generic api server. // ServerRunOptions contains the options while running a generic api server.
...@@ -62,21 +64,48 @@ type ServerRunOptions struct { ...@@ -62,21 +64,48 @@ type ServerRunOptions struct {
} }
func NewServerRunOptions() *ServerRunOptions { func NewServerRunOptions() *ServerRunOptions {
defaults := server.NewConfig()
return &ServerRunOptions{ return &ServerRunOptions{
AdmissionControl: "AlwaysAdmit", AdmissionControl: "AlwaysAdmit",
DefaultStorageMediaType: "application/json", DefaultStorageMediaType: "application/json",
DeleteCollectionWorkers: 1, DeleteCollectionWorkers: 1,
EnableGarbageCollection: true, EnableGarbageCollection: defaults.EnableGarbageCollection,
EnableProfiling: true, EnableProfiling: defaults.EnableProfiling,
EnableContentionProfiling: false, EnableContentionProfiling: false,
EnableWatchCache: true, EnableWatchCache: true,
MaxRequestsInFlight: 400, MaxRequestsInFlight: defaults.MaxRequestsInFlight,
MaxMutatingRequestsInFlight: 200, MaxMutatingRequestsInFlight: defaults.MaxMutatingRequestsInFlight,
MinRequestTimeout: 1800, MinRequestTimeout: defaults.MinRequestTimeout,
RuntimeConfig: make(utilflag.ConfigurationMap), RuntimeConfig: make(utilflag.ConfigurationMap),
} }
} }
// ApplyOptions applies the run options to the method receiver and returns self
func (s *ServerRunOptions) ApplyTo(c *server.Config) error {
if len(s.AuditLogPath) != 0 {
c.AuditWriter = &lumberjack.Logger{
Filename: s.AuditLogPath,
MaxAge: s.AuditLogMaxAge,
MaxBackups: s.AuditLogMaxBackups,
MaxSize: s.AuditLogMaxSize,
}
}
c.CorsAllowedOriginList = s.CorsAllowedOriginList
c.EnableGarbageCollection = s.EnableGarbageCollection
c.EnableProfiling = s.EnableProfiling
c.EnableContentionProfiling = s.EnableContentionProfiling
c.EnableSwaggerUI = s.EnableSwaggerUI
c.ExternalAddress = s.ExternalHost
c.MaxRequestsInFlight = s.MaxRequestsInFlight
c.MaxMutatingRequestsInFlight = s.MaxMutatingRequestsInFlight
c.MinRequestTimeout = s.MinRequestTimeout
c.PublicAddress = s.AdvertiseAddress
return nil
}
// DefaultAdvertiseAddress sets the field AdvertiseAddress if // DefaultAdvertiseAddress sets the field AdvertiseAddress if
// unset. The field will be set based on the SecureServingOptions. If // unset. The field will be set based on the SecureServingOptions. If
// the SecureServingOptions is not present, DefaultExternalAddress // the SecureServingOptions is not present, DefaultExternalAddress
......
...@@ -17,14 +17,19 @@ limitations under the License. ...@@ -17,14 +17,19 @@ limitations under the License.
package options package options
import ( import (
"crypto/tls"
"encoding/pem"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"path" "path"
"strconv"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/spf13/pflag" "github.com/spf13/pflag"
utilnet "k8s.io/apimachinery/pkg/util/net" utilnet "k8s.io/apimachinery/pkg/util/net"
"k8s.io/apiserver/pkg/server"
utilflag "k8s.io/apiserver/pkg/util/flag" utilflag "k8s.io/apiserver/pkg/util/flag"
certutil "k8s.io/client-go/util/cert" certutil "k8s.io/client-go/util/cert"
) )
...@@ -130,6 +135,70 @@ func (s *SecureServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) { ...@@ -130,6 +135,70 @@ func (s *SecureServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) {
fs.MarkDeprecated("public-address-override", "see --bind-address instead.") fs.MarkDeprecated("public-address-override", "see --bind-address instead.")
} }
func (s *SecureServingOptions) ApplyTo(c *server.Config) error {
if s.ServingOptions.BindPort <= 0 {
return nil
}
secureServingInfo := &server.SecureServingInfo{
ServingInfo: server.ServingInfo{
BindAddress: net.JoinHostPort(s.ServingOptions.BindAddress.String(), strconv.Itoa(s.ServingOptions.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.ServingOptions.BindPort
return nil
}
func NewInsecureServingOptions() *ServingOptions { func NewInsecureServingOptions() *ServingOptions {
return &ServingOptions{ return &ServingOptions{
BindAddress: net.ParseIP("127.0.0.1"), BindAddress: net.ParseIP("127.0.0.1"),
...@@ -172,6 +241,18 @@ func (s *ServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) { ...@@ -172,6 +241,18 @@ func (s *ServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) {
fs.MarkDeprecated("port", "see --insecure-port instead.") fs.MarkDeprecated("port", "see --insecure-port instead.")
} }
func (s *ServingOptions) ApplyTo(c *server.Config) error {
if s.BindPort <= 0 {
return nil
}
c.InsecureServingInfo = &server.ServingInfo{
BindAddress: net.JoinHostPort(s.BindAddress.String(), strconv.Itoa(s.BindPort)),
}
return nil
}
func (s *SecureServingOptions) MaybeDefaultWithSelfSignedCerts(publicAddress string, alternateIPs ...net.IP) error { func (s *SecureServingOptions) MaybeDefaultWithSelfSignedCerts(publicAddress string, alternateIPs ...net.IP) error {
if s == nil { if s == nil {
return nil return nil
......
...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and ...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package server package options
import ( import (
"crypto/tls" "crypto/tls"
...@@ -26,17 +26,30 @@ import ( ...@@ -26,17 +26,30 @@ import (
"net" "net"
"os" "os"
"reflect" "reflect"
"strconv"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"k8s.io/apiserver/pkg/server/options" "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"
utilflag "k8s.io/apiserver/pkg/util/flag" utilflag "k8s.io/apiserver/pkg/util/flag"
utilcert "k8s.io/client-go/util/cert" utilcert "k8s.io/client-go/util/cert"
"k8s.io/kubernetes/pkg/client/clientset_generated/clientset" "k8s.io/kubernetes/pkg/client/clientset_generated/clientset"
) )
func setUp(t *testing.T) Config {
scheme := runtime.NewScheme()
codecs := serializer.NewCodecFactory(scheme)
config := NewConfig().WithSerializer(codecs)
config.RequestContextMapper = genericapirequest.NewRequestContextMapper()
return *config
}
type TestCertSpec struct { type TestCertSpec struct {
host string host string
names, ips []string // in certificate names, ips []string // in certificate
...@@ -190,7 +203,7 @@ func TestGetNamedCertificateMap(t *testing.T) { ...@@ -190,7 +203,7 @@ func TestGetNamedCertificateMap(t *testing.T) {
NextTest: NextTest:
for i, test := range tests { for i, test := range tests {
var namedTLSCerts []namedTlsCert var namedTLSCerts []NamedTLSCert
bySignature := map[string]int{} // index in test.certs by cert signature bySignature := map[string]int{} // index in test.certs by cert signature
for j, c := range test.certs { for j, c := range test.certs {
cert, err := createTestTLSCerts(c.TestCertSpec) cert, err := createTestTLSCerts(c.TestCertSpec)
...@@ -199,9 +212,9 @@ NextTest: ...@@ -199,9 +212,9 @@ NextTest:
continue NextTest continue NextTest
} }
namedTLSCerts = append(namedTLSCerts, namedTlsCert{ namedTLSCerts = append(namedTLSCerts, NamedTLSCert{
tlsCert: cert, TLSCert: cert,
names: c.explicitNames, Names: c.explicitNames,
}) })
sig, err := certSignature(cert) sig, err := certSignature(cert)
...@@ -212,7 +225,7 @@ NextTest: ...@@ -212,7 +225,7 @@ NextTest:
bySignature[sig] = j bySignature[sig] = j
} }
certMap, err := getNamedCertificateMap(namedTLSCerts) certMap, err := GetNamedCertificateMap(namedTLSCerts)
if err == nil && len(test.errorString) != 0 { if err == nil && len(test.errorString) != 0 {
t.Errorf("%d - expected no error, got: %v", i, err) t.Errorf("%d - expected no error, got: %v", i, err)
} else if err != nil && err.Error() != test.errorString { } else if err != nil && err.Error() != test.errorString {
...@@ -461,27 +474,26 @@ NextTest: ...@@ -461,27 +474,26 @@ NextTest:
stopCh := make(chan struct{}) stopCh := make(chan struct{})
// launch server // launch server
etcdserver, config, _ := setUp(t) config := setUp(t)
defer etcdserver.Terminate(t)
v := fakeVersion() v := fakeVersion()
config.Version = &v config.Version = &v
config.EnableIndex = true config.EnableIndex = true
_, err = config.ApplySecureServingOptions(&options.SecureServingOptions{ secureOptions := &SecureServingOptions{
ServingOptions: options.ServingOptions{ ServingOptions: ServingOptions{
BindAddress: net.ParseIP("127.0.0.1"), BindAddress: net.ParseIP("127.0.0.1"),
BindPort: 6443, BindPort: 6443,
}, },
ServerCert: options.GeneratableKeyCert{ ServerCert: GeneratableKeyCert{
CertKey: options.CertKey{ CertKey: CertKey{
CertFile: serverCertBundleFile, CertFile: serverCertBundleFile,
KeyFile: serverKeyFile, KeyFile: serverKeyFile,
}, },
}, },
SNICertKeys: namedCertKeys, SNICertKeys: namedCertKeys,
}) }
if err != nil { if err := secureOptions.ApplyTo(&config); err != nil {
t.Errorf("%q - failed applying the SecureServingOptions: %v", title, err) t.Errorf("%q - failed applying the SecureServingOptions: %v", title, err)
continue NextTest continue NextTest
} }
...@@ -496,10 +508,14 @@ NextTest: ...@@ -496,10 +508,14 @@ NextTest:
// patch in a 0-port to enable auto port allocation // patch in a 0-port to enable auto port allocation
s.SecureServingInfo.BindAddress = "127.0.0.1:0" s.SecureServingInfo.BindAddress = "127.0.0.1:0"
if err := s.serveSecurely(stopCh); err != nil { // add poststart hook to know when the server is up.
t.Errorf("%q - failed running the server: %v", title, err) startedCh := make(chan struct{})
continue NextTest s.AddPostStartHook("test-notifier", func(context PostStartHookContext) error {
} close(startedCh)
return nil
})
preparedServer := s.PrepareRun()
go preparedServer.Run(stopCh)
// load ca certificates into a pool // load ca certificates into a pool
roots := x509.NewCertPool() roots := x509.NewCertPool()
...@@ -507,8 +523,11 @@ NextTest: ...@@ -507,8 +523,11 @@ NextTest:
roots.AddCert(caCert) roots.AddCert(caCert)
} }
<-startedCh
effectiveSecurePort := fmt.Sprintf("%d", preparedServer.EffectiveSecurePort())
// try to dial // try to dial
addr := fmt.Sprintf("localhost:%d", s.effectiveSecurePort) addr := fmt.Sprintf("localhost:%s", effectiveSecurePort)
t.Logf("Dialing %s as %q", addr, test.ServerName) t.Logf("Dialing %s as %q", addr, test.ServerName)
conn, err := tls.Dial("tcp", addr, &tls.Config{ conn, err := tls.Dial("tcp", addr, &tls.Config{
RootCAs: roots, RootCAs: roots,
...@@ -536,7 +555,7 @@ NextTest: ...@@ -536,7 +555,7 @@ NextTest:
if len(test.SelfClientBindAddressOverride) != 0 { if len(test.SelfClientBindAddressOverride) != 0 {
host = test.SelfClientBindAddressOverride host = test.SelfClientBindAddressOverride
} }
config.SecureServingInfo.ServingInfo.BindAddress = net.JoinHostPort(host, strconv.Itoa(s.effectiveSecurePort)) config.SecureServingInfo.ServingInfo.BindAddress = net.JoinHostPort(host, effectiveSecurePort)
cfg, err := config.SecureServingInfo.NewSelfClientConfig("some-token") cfg, err := config.SecureServingInfo.NewSelfClientConfig("some-token")
if test.ExpectSelfClientError { if test.ExpectSelfClientError {
if err == nil { if err == nil {
...@@ -654,3 +673,13 @@ func certSignature(cert tls.Certificate) (string, error) { ...@@ -654,3 +673,13 @@ func certSignature(cert tls.Certificate) (string, error) {
} }
return x509CertSignature(x509Certs[0]), nil return x509CertSignature(x509Certs[0]), nil
} }
func fakeVersion() version.Info {
return version.Info{
Major: "42",
Minor: "42",
GitVersion: "42",
GitCommit: "34973274ccef6ab4dfaaf86599792fa9c3fe4689",
GitTreeState: "Dirty",
}
}
...@@ -173,25 +173,25 @@ func runServer(server *http.Server, network string, stopCh <-chan struct{}) (int ...@@ -173,25 +173,25 @@ func runServer(server *http.Server, network string, stopCh <-chan struct{}) (int
return tcpAddr.Port, nil return tcpAddr.Port, nil
} }
type namedTlsCert struct { type NamedTLSCert struct {
tlsCert tls.Certificate TLSCert tls.Certificate
// names is a list of domain patterns: fully qualified domain names, possibly prefixed with // names is a list of domain patterns: fully qualified domain names, possibly prefixed with
// wildcard segments. // wildcard segments.
names []string Names []string
} }
// getNamedCertificateMap returns a map of *tls.Certificate by name. It's is // 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 // suitable for use in tls.Config#NamedCertificates. Returns an error if any of the certs
// cannot be loaded. Returns nil if len(certs) == 0 // cannot be loaded. Returns nil if len(certs) == 0
func getNamedCertificateMap(certs []namedTlsCert) (map[string]*tls.Certificate, error) { func GetNamedCertificateMap(certs []NamedTLSCert) (map[string]*tls.Certificate, error) {
// register certs with implicit names first, reverse order such that earlier trump over the later // register certs with implicit names first, reverse order such that earlier trump over the later
byName := map[string]*tls.Certificate{} byName := map[string]*tls.Certificate{}
for i := len(certs) - 1; i >= 0; i-- { for i := len(certs) - 1; i >= 0; i-- {
if len(certs[i].names) > 0 { if len(certs[i].Names) > 0 {
continue continue
} }
cert := &certs[i].tlsCert cert := &certs[i].TLSCert
// read names from certificate common names and DNS names // read names from certificate common names and DNS names
if len(cert.Certificate) == 0 { if len(cert.Certificate) == 0 {
...@@ -216,8 +216,8 @@ func getNamedCertificateMap(certs []namedTlsCert) (map[string]*tls.Certificate, ...@@ -216,8 +216,8 @@ func getNamedCertificateMap(certs []namedTlsCert) (map[string]*tls.Certificate,
// again in reverse order. // again in reverse order.
for i := len(certs) - 1; i >= 0; i-- { for i := len(certs) - 1; i >= 0; i-- {
namedCert := &certs[i] namedCert := &certs[i]
for _, name := range namedCert.names { for _, name := range namedCert.Names {
byName[name] = &certs[i].tlsCert byName[name] = &certs[i].TLSCert
} }
} }
......
...@@ -28,7 +28,6 @@ import ( ...@@ -28,7 +28,6 @@ import (
"k8s.io/apiserver/pkg/apis/example" "k8s.io/apiserver/pkg/apis/example"
exampleinstall "k8s.io/apiserver/pkg/apis/example/install" exampleinstall "k8s.io/apiserver/pkg/apis/example/install"
examplev1 "k8s.io/apiserver/pkg/apis/example/v1" examplev1 "k8s.io/apiserver/pkg/apis/example/v1"
"k8s.io/apiserver/pkg/server/options"
"k8s.io/apiserver/pkg/storage/storagebackend" "k8s.io/apiserver/pkg/storage/storagebackend"
) )
...@@ -128,7 +127,7 @@ func TestUpdateEtcdOverrides(t *testing.T) { ...@@ -128,7 +127,7 @@ func TestUpdateEtcdOverrides(t *testing.T) {
defaultEtcdLocation := []string{"http://127.0.0.1"} defaultEtcdLocation := []string{"http://127.0.0.1"}
for i, test := range testCases { for i, test := range testCases {
defaultConfig := storagebackend.Config{ defaultConfig := storagebackend.Config{
Prefix: options.DefaultEtcdPathPrefix, Prefix: "/registry",
ServerList: defaultEtcdLocation, ServerList: defaultEtcdLocation,
Copier: scheme, Copier: scheme,
} }
......
...@@ -9072,7 +9072,6 @@ go_library( ...@@ -9072,7 +9072,6 @@ go_library(
"//vendor:github.com/golang/glog", "//vendor:github.com/golang/glog",
"//vendor:github.com/pborman/uuid", "//vendor:github.com/pborman/uuid",
"//vendor:github.com/pkg/errors", "//vendor:github.com/pkg/errors",
"//vendor:gopkg.in/natefinch/lumberjack.v2",
"//vendor:k8s.io/apimachinery/pkg/apimachinery", "//vendor:k8s.io/apimachinery/pkg/apimachinery",
"//vendor:k8s.io/apimachinery/pkg/apimachinery/registered", "//vendor:k8s.io/apimachinery/pkg/apimachinery/registered",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
...@@ -9103,7 +9102,6 @@ go_library( ...@@ -9103,7 +9102,6 @@ go_library(
"//vendor:k8s.io/apiserver/pkg/server/filters", "//vendor:k8s.io/apiserver/pkg/server/filters",
"//vendor:k8s.io/apiserver/pkg/server/healthz", "//vendor:k8s.io/apiserver/pkg/server/healthz",
"//vendor:k8s.io/apiserver/pkg/server/mux", "//vendor:k8s.io/apiserver/pkg/server/mux",
"//vendor:k8s.io/apiserver/pkg/server/options",
"//vendor:k8s.io/apiserver/pkg/server/routes", "//vendor:k8s.io/apiserver/pkg/server/routes",
"//vendor:k8s.io/apiserver/pkg/storage/storagebackend", "//vendor:k8s.io/apiserver/pkg/storage/storagebackend",
"//vendor:k8s.io/client-go/rest", "//vendor:k8s.io/client-go/rest",
...@@ -14102,6 +14100,7 @@ go_library( ...@@ -14102,6 +14100,7 @@ go_library(
deps = [ deps = [
"//vendor:github.com/golang/glog", "//vendor:github.com/golang/glog",
"//vendor:github.com/spf13/pflag", "//vendor:github.com/spf13/pflag",
"//vendor:gopkg.in/natefinch/lumberjack.v2",
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
"//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/util/net", "//vendor:k8s.io/apimachinery/pkg/util/net",
...@@ -14109,6 +14108,7 @@ go_library( ...@@ -14109,6 +14108,7 @@ go_library(
"//vendor:k8s.io/apiserver/pkg/authentication/authenticatorfactory", "//vendor:k8s.io/apiserver/pkg/authentication/authenticatorfactory",
"//vendor:k8s.io/apiserver/pkg/authorization/authorizerfactory", "//vendor:k8s.io/apiserver/pkg/authorization/authorizerfactory",
"//vendor:k8s.io/apiserver/pkg/features", "//vendor:k8s.io/apiserver/pkg/features",
"//vendor:k8s.io/apiserver/pkg/server",
"//vendor:k8s.io/apiserver/pkg/storage/storagebackend", "//vendor:k8s.io/apiserver/pkg/storage/storagebackend",
"//vendor:k8s.io/apiserver/pkg/util/feature", "//vendor:k8s.io/apiserver/pkg/util/feature",
"//vendor:k8s.io/apiserver/pkg/util/flag", "//vendor:k8s.io/apiserver/pkg/util/flag",
...@@ -14775,13 +14775,11 @@ go_test( ...@@ -14775,13 +14775,11 @@ go_test(
srcs = [ srcs = [
"k8s.io/apiserver/pkg/server/genericapiserver_test.go", "k8s.io/apiserver/pkg/server/genericapiserver_test.go",
"k8s.io/apiserver/pkg/server/resource_config_test.go", "k8s.io/apiserver/pkg/server/resource_config_test.go",
"k8s.io/apiserver/pkg/server/serve_test.go",
"k8s.io/apiserver/pkg/server/storage_factory_test.go", "k8s.io/apiserver/pkg/server/storage_factory_test.go",
], ],
library = ":k8s.io/apiserver/pkg/server", library = ":k8s.io/apiserver/pkg/server",
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/client/clientset_generated/clientset:go_default_library",
"//pkg/generated/openapi:go_default_library", "//pkg/generated/openapi:go_default_library",
"//vendor:github.com/go-openapi/spec", "//vendor:github.com/go-openapi/spec",
"//vendor:github.com/stretchr/testify/assert", "//vendor:github.com/stretchr/testify/assert",
...@@ -14803,12 +14801,9 @@ go_test( ...@@ -14803,12 +14801,9 @@ go_test(
"//vendor:k8s.io/apiserver/pkg/authorization/authorizer", "//vendor:k8s.io/apiserver/pkg/authorization/authorizer",
"//vendor:k8s.io/apiserver/pkg/endpoints/request", "//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/registry/rest", "//vendor:k8s.io/apiserver/pkg/registry/rest",
"//vendor:k8s.io/apiserver/pkg/server/options",
"//vendor:k8s.io/apiserver/pkg/storage/etcd/testing", "//vendor:k8s.io/apiserver/pkg/storage/etcd/testing",
"//vendor:k8s.io/apiserver/pkg/storage/storagebackend", "//vendor:k8s.io/apiserver/pkg/storage/storagebackend",
"//vendor:k8s.io/apiserver/pkg/util/flag",
"//vendor:k8s.io/client-go/pkg/api", "//vendor:k8s.io/client-go/pkg/api",
"//vendor:k8s.io/client-go/util/cert",
], ],
) )
...@@ -15257,3 +15252,21 @@ go_library( ...@@ -15257,3 +15252,21 @@ go_library(
"//vendor:k8s.io/apiserver/pkg/apis/apiserver", "//vendor:k8s.io/apiserver/pkg/apis/apiserver",
], ],
) )
go_test(
name = "k8s.io/apiserver/pkg/server/options_test",
srcs = ["k8s.io/apiserver/pkg/server/options/serving_test.go"],
library = ":k8s.io/apiserver/pkg/server/options",
tags = ["automanaged"],
deps = [
"//pkg/client/clientset_generated/clientset:go_default_library",
"//vendor:github.com/stretchr/testify/assert",
"//vendor:k8s.io/apimachinery/pkg/runtime",
"//vendor:k8s.io/apimachinery/pkg/runtime/serializer",
"//vendor:k8s.io/apimachinery/pkg/version",
"//vendor:k8s.io/apiserver/pkg/endpoints/request",
"//vendor:k8s.io/apiserver/pkg/server",
"//vendor:k8s.io/apiserver/pkg/util/flag",
"//vendor:k8s.io/client-go/util/cert",
],
)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment