mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-07 19:23:40 +00:00
MOVE: cmd/kube-apiserver/app/options: split apart controlplane part
This commit is contained in:
parent
9f5a3f5e90
commit
1b3779baa0
@ -19,141 +19,78 @@ package options
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
serveroptions "k8s.io/apiserver/pkg/server/options"
|
apiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||||
"k8s.io/client-go/util/keyutil"
|
|
||||||
_ "k8s.io/component-base/metrics/prometheus/workqueue"
|
_ "k8s.io/component-base/metrics/prometheus/workqueue"
|
||||||
"k8s.io/klog/v2"
|
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
|
|
||||||
"k8s.io/kubernetes/pkg/controlplane"
|
controlplane "k8s.io/kubernetes/pkg/controlplane/apiserver/options"
|
||||||
"k8s.io/kubernetes/pkg/kubeapiserver"
|
"k8s.io/kubernetes/pkg/kubeapiserver"
|
||||||
kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
|
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
||||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// completedOptions is a private wrapper that enforces a call of Complete() before Run can be invoked.
|
// completedOptions is a private wrapper that enforces a call of Complete() before Run can be invoked.
|
||||||
type completedOptions struct {
|
type completedOptions struct {
|
||||||
*ServerRunOptions
|
controlplane.CompletedOptions
|
||||||
|
CloudProvider *kubeoptions.CloudProviderOptions
|
||||||
|
|
||||||
|
Extra
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompletedOptions struct {
|
type CompletedOptions struct {
|
||||||
completedOptions
|
// Embed a private pointer that cannot be instantiated outside of this package.
|
||||||
|
*completedOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete set default ServerRunOptions.
|
// Complete set default ServerRunOptions.
|
||||||
// Should be called after kube-apiserver flags parsed.
|
// Should be called after kube-apiserver flags parsed.
|
||||||
func Complete(opts *ServerRunOptions) (CompletedOptions, error) {
|
func Complete(opts *ServerRunOptions) (CompletedOptions, error) {
|
||||||
// set defaults
|
if opts == nil {
|
||||||
if err := opts.GenericServerRunOptions.DefaultAdvertiseAddress(opts.SecureServing.SecureServingOptions); err != nil {
|
return CompletedOptions{completedOptions: &completedOptions{}}, nil
|
||||||
return CompletedOptions{}, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// process s.ServiceClusterIPRange from list to Primary and Secondary
|
// process opts.ServiceClusterIPRange from list to Primary and Secondary
|
||||||
// we process secondary only if provided by user
|
// we process secondary only if provided by user
|
||||||
apiServerServiceIP, primaryServiceIPRange, secondaryServiceIPRange, err := getServiceIPAndRanges(opts.ServiceClusterIPRanges)
|
apiServerServiceIP, primaryServiceIPRange, secondaryServiceIPRange, err := getServiceIPAndRanges(opts.ServiceClusterIPRanges)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CompletedOptions{}, err
|
return CompletedOptions{}, err
|
||||||
}
|
}
|
||||||
opts.PrimaryServiceClusterIPRange = primaryServiceIPRange
|
controlplane, err := opts.Options.Complete([]string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"}, []net.IP{apiServerServiceIP})
|
||||||
opts.SecondaryServiceClusterIPRange = secondaryServiceIPRange
|
if err != nil {
|
||||||
opts.APIServerServiceIP = apiServerServiceIP
|
return CompletedOptions{}, err
|
||||||
|
|
||||||
if err := opts.SecureServing.MaybeDefaultWithSelfSignedCerts(opts.GenericServerRunOptions.AdvertiseAddress.String(), []string{"kubernetes.default.svc", "kubernetes.default", "kubernetes"}, []net.IP{apiServerServiceIP}); err != nil {
|
|
||||||
return CompletedOptions{}, fmt.Errorf("error creating self-signed certificates: %v", err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.GenericServerRunOptions.ExternalHost) == 0 {
|
completed := completedOptions{
|
||||||
if len(opts.GenericServerRunOptions.AdvertiseAddress) > 0 {
|
CompletedOptions: controlplane,
|
||||||
opts.GenericServerRunOptions.ExternalHost = opts.GenericServerRunOptions.AdvertiseAddress.String()
|
CloudProvider: opts.CloudProvider,
|
||||||
} else {
|
|
||||||
hostname, err := os.Hostname()
|
Extra: opts.Extra,
|
||||||
if err != nil {
|
|
||||||
return CompletedOptions{}, fmt.Errorf("error finding host name: %v", err)
|
|
||||||
}
|
|
||||||
opts.GenericServerRunOptions.ExternalHost = hostname
|
|
||||||
}
|
|
||||||
klog.Infof("external host was not specified, using %v", opts.GenericServerRunOptions.ExternalHost)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.Authentication.ApplyAuthorization(opts.Authorization)
|
completed.PrimaryServiceClusterIPRange = primaryServiceIPRange
|
||||||
|
completed.SecondaryServiceClusterIPRange = secondaryServiceIPRange
|
||||||
|
completed.APIServerServiceIP = apiServerServiceIP
|
||||||
|
|
||||||
// Use (ServiceAccountSigningKeyFile != "") as a proxy to the user enabling
|
if completed.Etcd != nil && completed.Etcd.EnableWatchCache {
|
||||||
// TokenRequest functionality. This defaulting was convenient, but messed up
|
|
||||||
// a lot of people when they rotated their serving cert with no idea it was
|
|
||||||
// connected to their service account keys. We are taking this opportunity to
|
|
||||||
// remove this problematic defaulting.
|
|
||||||
if opts.ServiceAccountSigningKeyFile == "" {
|
|
||||||
// Default to the private server key for service account token signing
|
|
||||||
if len(opts.Authentication.ServiceAccounts.KeyFiles) == 0 && opts.SecureServing.ServerCert.CertKey.KeyFile != "" {
|
|
||||||
if kubeauthenticator.IsValidServiceAccountKeyFile(opts.SecureServing.ServerCert.CertKey.KeyFile) {
|
|
||||||
opts.Authentication.ServiceAccounts.KeyFiles = []string{opts.SecureServing.ServerCert.CertKey.KeyFile}
|
|
||||||
} else {
|
|
||||||
klog.Warning("No TLS key provided, service account token authentication disabled")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.ServiceAccountSigningKeyFile != "" && len(opts.Authentication.ServiceAccounts.Issuers) != 0 && opts.Authentication.ServiceAccounts.Issuers[0] != "" {
|
|
||||||
sk, err := keyutil.PrivateKeyFromFile(opts.ServiceAccountSigningKeyFile)
|
|
||||||
if err != nil {
|
|
||||||
return CompletedOptions{}, fmt.Errorf("failed to parse service-account-issuer-key-file: %v", err)
|
|
||||||
}
|
|
||||||
if opts.Authentication.ServiceAccounts.MaxExpiration != 0 {
|
|
||||||
lowBound := time.Hour
|
|
||||||
upBound := time.Duration(1<<32) * time.Second
|
|
||||||
if opts.Authentication.ServiceAccounts.MaxExpiration < lowBound ||
|
|
||||||
opts.Authentication.ServiceAccounts.MaxExpiration > upBound {
|
|
||||||
return CompletedOptions{}, fmt.Errorf("the service-account-max-token-expiration must be between 1 hour and 2^32 seconds")
|
|
||||||
}
|
|
||||||
if opts.Authentication.ServiceAccounts.ExtendExpiration {
|
|
||||||
if opts.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.WarnOnlyBoundTokenExpirationSeconds*time.Second {
|
|
||||||
klog.Warningf("service-account-extend-token-expiration is true, in order to correctly trigger safe transition logic, service-account-max-token-expiration must be set longer than %d seconds (currently %s)", serviceaccount.WarnOnlyBoundTokenExpirationSeconds, opts.Authentication.ServiceAccounts.MaxExpiration)
|
|
||||||
}
|
|
||||||
if opts.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.ExpirationExtensionSeconds*time.Second {
|
|
||||||
klog.Warningf("service-account-extend-token-expiration is true, enabling tokens valid up to %d seconds, which is longer than service-account-max-token-expiration set to %s seconds", serviceaccount.ExpirationExtensionSeconds, opts.Authentication.ServiceAccounts.MaxExpiration)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
opts.ServiceAccountIssuer, err = serviceaccount.JWTTokenGenerator(opts.Authentication.ServiceAccounts.Issuers[0], sk)
|
|
||||||
if err != nil {
|
|
||||||
return CompletedOptions{}, fmt.Errorf("failed to build token generator: %v", err)
|
|
||||||
}
|
|
||||||
opts.ServiceAccountTokenMaxExpiration = opts.Authentication.ServiceAccounts.MaxExpiration
|
|
||||||
}
|
|
||||||
|
|
||||||
if opts.Etcd.EnableWatchCache {
|
|
||||||
sizes := kubeapiserver.DefaultWatchCacheSizes()
|
sizes := kubeapiserver.DefaultWatchCacheSizes()
|
||||||
// Ensure that overrides parse correctly.
|
// Ensure that overrides parse correctly.
|
||||||
userSpecified, err := serveroptions.ParseWatchCacheSizes(opts.Etcd.WatchCacheSizes)
|
userSpecified, err := apiserveroptions.ParseWatchCacheSizes(completed.Etcd.WatchCacheSizes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CompletedOptions{}, err
|
return CompletedOptions{}, err
|
||||||
}
|
}
|
||||||
for resource, size := range userSpecified {
|
for resource, size := range userSpecified {
|
||||||
sizes[resource] = size
|
sizes[resource] = size
|
||||||
}
|
}
|
||||||
opts.Etcd.WatchCacheSizes, err = serveroptions.WriteWatchCacheSizes(sizes)
|
completed.Etcd.WatchCacheSizes, err = apiserveroptions.WriteWatchCacheSizes(sizes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return CompletedOptions{}, err
|
return CompletedOptions{}, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value := range opts.APIEnablement.RuntimeConfig {
|
return CompletedOptions{
|
||||||
if key == "v1" || strings.HasPrefix(key, "v1/") ||
|
completedOptions: &completed,
|
||||||
key == "api/v1" || strings.HasPrefix(key, "api/v1/") {
|
}, nil
|
||||||
delete(opts.APIEnablement.RuntimeConfig, key)
|
|
||||||
opts.APIEnablement.RuntimeConfig["/v1"] = value
|
|
||||||
}
|
|
||||||
if key == "api/legacy" {
|
|
||||||
delete(opts.APIEnablement.RuntimeConfig, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompletedOptions{completedOptions: completedOptions{ServerRunOptions: opts}}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getServiceIPAndRanges(serviceClusterIPRanges string) (net.IP, net.IPNet, net.IPNet, error) {
|
func getServiceIPAndRanges(serviceClusterIPRanges string) (net.IP, net.IPNet, net.IPNet, error) {
|
||||||
|
@ -23,45 +23,29 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
|
||||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
|
||||||
cliflag "k8s.io/component-base/cli/flag"
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
"k8s.io/component-base/logs"
|
|
||||||
"k8s.io/component-base/metrics"
|
|
||||||
|
|
||||||
logsapi "k8s.io/component-base/logs/api/v1"
|
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/cluster/ports"
|
"k8s.io/kubernetes/pkg/cluster/ports"
|
||||||
|
controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver/options"
|
||||||
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
||||||
_ "k8s.io/kubernetes/pkg/features" // add the kubernetes feature gates
|
_ "k8s.io/kubernetes/pkg/features" // add the kubernetes feature gates
|
||||||
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
||||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
"k8s.io/kubernetes/pkg/serviceaccount"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServerRunOptions runs a kubernetes api server.
|
// ServerRunOptions runs a kubernetes api server.
|
||||||
type ServerRunOptions struct {
|
type ServerRunOptions struct {
|
||||||
GenericServerRunOptions *genericoptions.ServerRunOptions
|
*controlplaneapiserver.Options // embedded to avoid noise in existing consumers
|
||||||
Etcd *genericoptions.EtcdOptions
|
CloudProvider *kubeoptions.CloudProviderOptions
|
||||||
SecureServing *genericoptions.SecureServingOptionsWithLoopback
|
|
||||||
Audit *genericoptions.AuditOptions
|
|
||||||
Features *genericoptions.FeatureOptions
|
|
||||||
Admission *kubeoptions.AdmissionOptions
|
|
||||||
Authentication *kubeoptions.BuiltInAuthenticationOptions
|
|
||||||
Authorization *kubeoptions.BuiltInAuthorizationOptions
|
|
||||||
CloudProvider *kubeoptions.CloudProviderOptions
|
|
||||||
APIEnablement *genericoptions.APIEnablementOptions
|
|
||||||
EgressSelector *genericoptions.EgressSelectorOptions
|
|
||||||
Metrics *metrics.Options
|
|
||||||
Logs *logs.Options
|
|
||||||
Traces *genericoptions.TracingOptions
|
|
||||||
|
|
||||||
|
Extra
|
||||||
|
}
|
||||||
|
|
||||||
|
type Extra struct {
|
||||||
AllowPrivileged bool
|
AllowPrivileged bool
|
||||||
EnableLogsHandler bool
|
|
||||||
EventTTL time.Duration
|
|
||||||
KubeletConfig kubeletclient.KubeletClientConfig
|
KubeletConfig kubeletclient.KubeletClientConfig
|
||||||
KubernetesServiceNodePort int
|
KubernetesServiceNodePort int
|
||||||
MaxConnectionBytesPerSec int64
|
|
||||||
// ServiceClusterIPRange is mapped to input provided by user
|
// ServiceClusterIPRange is mapped to input provided by user
|
||||||
ServiceClusterIPRanges string
|
ServiceClusterIPRanges string
|
||||||
// PrimaryServiceClusterIPRange and SecondaryServiceClusterIPRange are the results
|
// PrimaryServiceClusterIPRange and SecondaryServiceClusterIPRange are the results
|
||||||
@ -73,110 +57,53 @@ type ServerRunOptions struct {
|
|||||||
|
|
||||||
ServiceNodePortRange utilnet.PortRange
|
ServiceNodePortRange utilnet.PortRange
|
||||||
|
|
||||||
ProxyClientCertFile string
|
|
||||||
ProxyClientKeyFile string
|
|
||||||
|
|
||||||
EnableAggregatorRouting bool
|
|
||||||
AggregatorRejectForwardingRedirects bool
|
|
||||||
|
|
||||||
MasterCount int
|
|
||||||
EndpointReconcilerType string
|
EndpointReconcilerType string
|
||||||
|
|
||||||
ServiceAccountSigningKeyFile string
|
|
||||||
ServiceAccountIssuer serviceaccount.TokenGenerator
|
|
||||||
ServiceAccountTokenMaxExpiration time.Duration
|
|
||||||
|
|
||||||
ShowHiddenMetricsForVersion string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServerRunOptions creates a new ServerRunOptions object with default parameters
|
// NewServerRunOptions creates a new ServerRunOptions object with default parameters
|
||||||
func NewServerRunOptions() *ServerRunOptions {
|
func NewServerRunOptions() *ServerRunOptions {
|
||||||
s := ServerRunOptions{
|
s := ServerRunOptions{
|
||||||
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
|
Options: controlplaneapiserver.NewOptions(),
|
||||||
Etcd: genericoptions.NewEtcdOptions(storagebackend.NewDefaultConfig(kubeoptions.DefaultEtcdPathPrefix, nil)),
|
CloudProvider: kubeoptions.NewCloudProviderOptions(),
|
||||||
SecureServing: kubeoptions.NewSecureServingOptions(),
|
|
||||||
Audit: genericoptions.NewAuditOptions(),
|
|
||||||
Features: genericoptions.NewFeatureOptions(),
|
|
||||||
Admission: kubeoptions.NewAdmissionOptions(),
|
|
||||||
Authentication: kubeoptions.NewBuiltInAuthenticationOptions().WithAll(),
|
|
||||||
Authorization: kubeoptions.NewBuiltInAuthorizationOptions(),
|
|
||||||
CloudProvider: kubeoptions.NewCloudProviderOptions(),
|
|
||||||
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
|
||||||
EgressSelector: genericoptions.NewEgressSelectorOptions(),
|
|
||||||
Metrics: metrics.NewOptions(),
|
|
||||||
Logs: logs.NewOptions(),
|
|
||||||
Traces: genericoptions.NewTracingOptions(),
|
|
||||||
|
|
||||||
EnableLogsHandler: true,
|
Extra: Extra{
|
||||||
EventTTL: 1 * time.Hour,
|
EndpointReconcilerType: string(reconcilers.LeaseEndpointReconcilerType),
|
||||||
MasterCount: 1,
|
KubeletConfig: kubeletclient.KubeletClientConfig{
|
||||||
EndpointReconcilerType: string(reconcilers.LeaseEndpointReconcilerType),
|
Port: ports.KubeletPort,
|
||||||
KubeletConfig: kubeletclient.KubeletClientConfig{
|
ReadOnlyPort: ports.KubeletReadOnlyPort,
|
||||||
Port: ports.KubeletPort,
|
PreferredAddressTypes: []string{
|
||||||
ReadOnlyPort: ports.KubeletReadOnlyPort,
|
// --override-hostname
|
||||||
PreferredAddressTypes: []string{
|
string(api.NodeHostName),
|
||||||
// --override-hostname
|
|
||||||
string(api.NodeHostName),
|
|
||||||
|
|
||||||
// internal, preferring DNS if reported
|
// internal, preferring DNS if reported
|
||||||
string(api.NodeInternalDNS),
|
string(api.NodeInternalDNS),
|
||||||
string(api.NodeInternalIP),
|
string(api.NodeInternalIP),
|
||||||
|
|
||||||
// external, preferring DNS if reported
|
// external, preferring DNS if reported
|
||||||
string(api.NodeExternalDNS),
|
string(api.NodeExternalDNS),
|
||||||
string(api.NodeExternalIP),
|
string(api.NodeExternalIP),
|
||||||
|
},
|
||||||
|
HTTPTimeout: time.Duration(5) * time.Second,
|
||||||
},
|
},
|
||||||
HTTPTimeout: time.Duration(5) * time.Second,
|
ServiceNodePortRange: kubeoptions.DefaultServiceNodePortRange,
|
||||||
},
|
},
|
||||||
ServiceNodePortRange: kubeoptions.DefaultServiceNodePortRange,
|
|
||||||
AggregatorRejectForwardingRedirects: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Overwrite the default for storage data format.
|
|
||||||
s.Etcd.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf"
|
|
||||||
|
|
||||||
return &s
|
return &s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flags returns flags for a specific APIServer by section name
|
// Flags returns flags for a specific APIServer by section name
|
||||||
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
||||||
// Add the generic flags.
|
s.Options.AddFlags(&fss)
|
||||||
s.GenericServerRunOptions.AddUniversalFlags(fss.FlagSet("generic"))
|
|
||||||
s.Etcd.AddFlags(fss.FlagSet("etcd"))
|
|
||||||
s.SecureServing.AddFlags(fss.FlagSet("secure serving"))
|
|
||||||
s.Audit.AddFlags(fss.FlagSet("auditing"))
|
|
||||||
s.Features.AddFlags(fss.FlagSet("features"))
|
|
||||||
s.Authentication.AddFlags(fss.FlagSet("authentication"))
|
|
||||||
s.Authorization.AddFlags(fss.FlagSet("authorization"))
|
|
||||||
s.CloudProvider.AddFlags(fss.FlagSet("cloud provider"))
|
s.CloudProvider.AddFlags(fss.FlagSet("cloud provider"))
|
||||||
s.APIEnablement.AddFlags(fss.FlagSet("API enablement"))
|
|
||||||
s.EgressSelector.AddFlags(fss.FlagSet("egress selector"))
|
|
||||||
s.Admission.AddFlags(fss.FlagSet("admission"))
|
|
||||||
s.Metrics.AddFlags(fss.FlagSet("metrics"))
|
|
||||||
logsapi.AddFlags(s.Logs, fss.FlagSet("logs"))
|
|
||||||
s.Traces.AddFlags(fss.FlagSet("traces"))
|
|
||||||
|
|
||||||
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||||
// arrange these text blocks sensibly. Grrr.
|
// arrange these text blocks sensibly. Grrr.
|
||||||
fs := fss.FlagSet("misc")
|
fs := fss.FlagSet("misc")
|
||||||
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL,
|
|
||||||
"Amount of time to retain events.")
|
|
||||||
|
|
||||||
fs.BoolVar(&s.AllowPrivileged, "allow-privileged", s.AllowPrivileged,
|
fs.BoolVar(&s.AllowPrivileged, "allow-privileged", s.AllowPrivileged,
|
||||||
"If true, allow privileged containers. [default=false]")
|
"If true, allow privileged containers. [default=false]")
|
||||||
|
|
||||||
fs.BoolVar(&s.EnableLogsHandler, "enable-logs-handler", s.EnableLogsHandler,
|
|
||||||
"If true, install a /logs handler for the apiserver logs.")
|
|
||||||
fs.MarkDeprecated("enable-logs-handler", "This flag will be removed in v1.19")
|
|
||||||
|
|
||||||
fs.Int64Var(&s.MaxConnectionBytesPerSec, "max-connection-bytes-per-sec", s.MaxConnectionBytesPerSec, ""+
|
|
||||||
"If non-zero, throttle each user connection to this number of bytes/sec. "+
|
|
||||||
"Currently only applies to long-running requests.")
|
|
||||||
|
|
||||||
fs.IntVar(&s.MasterCount, "apiserver-count", s.MasterCount,
|
|
||||||
"The number of apiservers running in the cluster, must be a positive number. (In use when --endpoint-reconciler-type=master-count is enabled.)")
|
|
||||||
fs.MarkDeprecated("apiserver-count", "apiserver-count is deprecated and will be removed in a future version.")
|
|
||||||
|
|
||||||
fs.StringVar(&s.EndpointReconcilerType, "endpoint-reconciler-type", s.EndpointReconcilerType,
|
fs.StringVar(&s.EndpointReconcilerType, "endpoint-reconciler-type", s.EndpointReconcilerType,
|
||||||
"Use an endpoint reconciler ("+strings.Join(reconcilers.AllTypes.Names(), ", ")+") master-count is deprecated, and will be removed in a future version.")
|
"Use an endpoint reconciler ("+strings.Join(reconcilers.AllTypes.Names(), ", ")+") master-count is deprecated, and will be removed in a future version.")
|
||||||
|
|
||||||
@ -219,27 +146,5 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
|||||||
fs.StringVar(&s.KubeletConfig.TLSClientConfig.CAFile, "kubelet-certificate-authority", s.KubeletConfig.TLSClientConfig.CAFile,
|
fs.StringVar(&s.KubeletConfig.TLSClientConfig.CAFile, "kubelet-certificate-authority", s.KubeletConfig.TLSClientConfig.CAFile,
|
||||||
"Path to a cert file for the certificate authority.")
|
"Path to a cert file for the certificate authority.")
|
||||||
|
|
||||||
fs.StringVar(&s.ProxyClientCertFile, "proxy-client-cert-file", s.ProxyClientCertFile, ""+
|
|
||||||
"Client certificate used to prove the identity of the aggregator or kube-apiserver "+
|
|
||||||
"when it must call out during a request. This includes proxying requests to a user "+
|
|
||||||
"api-server and calling out to webhook admission plugins. It is expected that this "+
|
|
||||||
"cert includes a signature from the CA in the --requestheader-client-ca-file flag. "+
|
|
||||||
"That CA is published in the 'extension-apiserver-authentication' configmap in "+
|
|
||||||
"the kube-system namespace. Components receiving calls from kube-aggregator should "+
|
|
||||||
"use that CA to perform their half of the mutual TLS verification.")
|
|
||||||
fs.StringVar(&s.ProxyClientKeyFile, "proxy-client-key-file", s.ProxyClientKeyFile, ""+
|
|
||||||
"Private key for the client certificate used to prove the identity of the aggregator or kube-apiserver "+
|
|
||||||
"when it must call out during a request. This includes proxying requests to a user "+
|
|
||||||
"api-server and calling out to webhook admission plugins.")
|
|
||||||
|
|
||||||
fs.BoolVar(&s.EnableAggregatorRouting, "enable-aggregator-routing", s.EnableAggregatorRouting,
|
|
||||||
"Turns on aggregator routing requests to endpoints IP rather than cluster IP.")
|
|
||||||
|
|
||||||
fs.BoolVar(&s.AggregatorRejectForwardingRedirects, "aggregator-reject-forwarding-redirect", s.AggregatorRejectForwardingRedirects,
|
|
||||||
"Aggregator reject forwarding redirect response back to client.")
|
|
||||||
|
|
||||||
fs.StringVar(&s.ServiceAccountSigningKeyFile, "service-account-signing-key-file", s.ServiceAccountSigningKeyFile, ""+
|
|
||||||
"Path to the file that contains the current private key of the service account token issuer. The issuer will sign issued ID tokens with this private key.")
|
|
||||||
|
|
||||||
return fss
|
return fss
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import (
|
|||||||
"k8s.io/component-base/logs"
|
"k8s.io/component-base/logs"
|
||||||
"k8s.io/component-base/metrics"
|
"k8s.io/component-base/metrics"
|
||||||
kapi "k8s.io/kubernetes/pkg/apis/core"
|
kapi "k8s.io/kubernetes/pkg/apis/core"
|
||||||
|
controlplaneapiserver "k8s.io/kubernetes/pkg/controlplane/apiserver/options"
|
||||||
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
"k8s.io/kubernetes/pkg/controlplane/reconcilers"
|
||||||
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
||||||
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
kubeletclient "k8s.io/kubernetes/pkg/kubelet/client"
|
||||||
@ -125,200 +126,205 @@ func TestAddFlags(t *testing.T) {
|
|||||||
|
|
||||||
// This is a snapshot of expected options parsed by args.
|
// This is a snapshot of expected options parsed by args.
|
||||||
expected := &ServerRunOptions{
|
expected := &ServerRunOptions{
|
||||||
ServiceNodePortRange: kubeoptions.DefaultServiceNodePortRange,
|
Options: &controlplaneapiserver.Options{
|
||||||
ServiceClusterIPRanges: (&net.IPNet{IP: netutils.ParseIPSloppy("192.168.128.0"), Mask: net.CIDRMask(17, 32)}).String(),
|
MasterCount: 5,
|
||||||
MasterCount: 5,
|
GenericServerRunOptions: &apiserveroptions.ServerRunOptions{
|
||||||
EndpointReconcilerType: string(reconcilers.LeaseEndpointReconcilerType),
|
AdvertiseAddress: netutils.ParseIPSloppy("192.168.10.10"),
|
||||||
AllowPrivileged: false,
|
CorsAllowedOriginList: []string{"10.10.10.100", "10.10.10.200"},
|
||||||
GenericServerRunOptions: &apiserveroptions.ServerRunOptions{
|
MaxRequestsInFlight: 400,
|
||||||
AdvertiseAddress: netutils.ParseIPSloppy("192.168.10.10"),
|
MaxMutatingRequestsInFlight: 200,
|
||||||
CorsAllowedOriginList: []string{"10.10.10.100", "10.10.10.200"},
|
RequestTimeout: time.Duration(2) * time.Minute,
|
||||||
MaxRequestsInFlight: 400,
|
MinRequestTimeout: 1800,
|
||||||
MaxMutatingRequestsInFlight: 200,
|
JSONPatchMaxCopyBytes: int64(3 * 1024 * 1024),
|
||||||
RequestTimeout: time.Duration(2) * time.Minute,
|
MaxRequestBodyBytes: int64(3 * 1024 * 1024),
|
||||||
MinRequestTimeout: 1800,
|
|
||||||
JSONPatchMaxCopyBytes: int64(3 * 1024 * 1024),
|
|
||||||
MaxRequestBodyBytes: int64(3 * 1024 * 1024),
|
|
||||||
},
|
|
||||||
Admission: &kubeoptions.AdmissionOptions{
|
|
||||||
GenericAdmission: &apiserveroptions.AdmissionOptions{
|
|
||||||
RecommendedPluginOrder: s.Admission.GenericAdmission.RecommendedPluginOrder,
|
|
||||||
DefaultOffPlugins: s.Admission.GenericAdmission.DefaultOffPlugins,
|
|
||||||
EnablePlugins: []string{"AlwaysDeny"},
|
|
||||||
ConfigFile: "/admission-control-config",
|
|
||||||
Plugins: s.Admission.GenericAdmission.Plugins,
|
|
||||||
Decorators: s.Admission.GenericAdmission.Decorators,
|
|
||||||
},
|
},
|
||||||
},
|
Admission: &kubeoptions.AdmissionOptions{
|
||||||
Etcd: &apiserveroptions.EtcdOptions{
|
GenericAdmission: &apiserveroptions.AdmissionOptions{
|
||||||
StorageConfig: storagebackend.Config{
|
RecommendedPluginOrder: s.Options.Admission.GenericAdmission.RecommendedPluginOrder,
|
||||||
Type: "etcd3",
|
DefaultOffPlugins: s.Options.Admission.GenericAdmission.DefaultOffPlugins,
|
||||||
Transport: storagebackend.TransportConfig{
|
EnablePlugins: []string{"AlwaysDeny"},
|
||||||
ServerList: nil,
|
ConfigFile: "/admission-control-config",
|
||||||
KeyFile: "/var/run/kubernetes/etcd.key",
|
Plugins: s.Options.Admission.GenericAdmission.Plugins,
|
||||||
TrustedCAFile: "/var/run/kubernetes/etcdca.crt",
|
Decorators: s.Options.Admission.GenericAdmission.Decorators,
|
||||||
CertFile: "/var/run/kubernetes/etcdce.crt",
|
|
||||||
TracerProvider: oteltrace.NewNoopTracerProvider(),
|
|
||||||
},
|
|
||||||
Paging: true,
|
|
||||||
Prefix: "/registry",
|
|
||||||
CompactionInterval: storagebackend.DefaultCompactInterval,
|
|
||||||
CountMetricPollPeriod: time.Minute,
|
|
||||||
DBMetricPollInterval: storagebackend.DefaultDBMetricPollInterval,
|
|
||||||
HealthcheckTimeout: storagebackend.DefaultHealthcheckTimeout,
|
|
||||||
ReadycheckTimeout: storagebackend.DefaultReadinessTimeout,
|
|
||||||
LeaseManagerConfig: etcd3.LeaseManagerConfig{
|
|
||||||
ReuseDurationSeconds: 100,
|
|
||||||
MaxObjectCount: 1000,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
DefaultStorageMediaType: "application/vnd.kubernetes.protobuf",
|
Etcd: &apiserveroptions.EtcdOptions{
|
||||||
DeleteCollectionWorkers: 1,
|
StorageConfig: storagebackend.Config{
|
||||||
EnableGarbageCollection: true,
|
Type: "etcd3",
|
||||||
EnableWatchCache: true,
|
Transport: storagebackend.TransportConfig{
|
||||||
DefaultWatchCacheSize: 100,
|
ServerList: nil,
|
||||||
},
|
KeyFile: "/var/run/kubernetes/etcd.key",
|
||||||
SecureServing: (&apiserveroptions.SecureServingOptions{
|
TrustedCAFile: "/var/run/kubernetes/etcdca.crt",
|
||||||
BindAddress: netutils.ParseIPSloppy("192.168.10.20"),
|
CertFile: "/var/run/kubernetes/etcdce.crt",
|
||||||
BindPort: 6443,
|
TracerProvider: oteltrace.NewNoopTracerProvider(),
|
||||||
ServerCert: apiserveroptions.GeneratableKeyCert{
|
},
|
||||||
CertDirectory: "/var/run/kubernetes",
|
Paging: true,
|
||||||
PairName: "apiserver",
|
Prefix: "/registry",
|
||||||
},
|
CompactionInterval: storagebackend.DefaultCompactInterval,
|
||||||
HTTP2MaxStreamsPerConnection: 42,
|
CountMetricPollPeriod: time.Minute,
|
||||||
Required: true,
|
DBMetricPollInterval: storagebackend.DefaultDBMetricPollInterval,
|
||||||
}).WithLoopback(),
|
HealthcheckTimeout: storagebackend.DefaultHealthcheckTimeout,
|
||||||
EventTTL: 1 * time.Hour,
|
ReadycheckTimeout: storagebackend.DefaultReadinessTimeout,
|
||||||
KubeletConfig: kubeletclient.KubeletClientConfig{
|
LeaseManagerConfig: etcd3.LeaseManagerConfig{
|
||||||
Port: 10250,
|
ReuseDurationSeconds: 100,
|
||||||
ReadOnlyPort: 10255,
|
MaxObjectCount: 1000,
|
||||||
PreferredAddressTypes: []string{
|
|
||||||
string(kapi.NodeHostName),
|
|
||||||
string(kapi.NodeInternalDNS),
|
|
||||||
string(kapi.NodeInternalIP),
|
|
||||||
string(kapi.NodeExternalDNS),
|
|
||||||
string(kapi.NodeExternalIP),
|
|
||||||
},
|
|
||||||
HTTPTimeout: time.Duration(5) * time.Second,
|
|
||||||
TLSClientConfig: kubeletclient.KubeletTLSConfig{
|
|
||||||
CertFile: "/var/run/kubernetes/ceserver.crt",
|
|
||||||
KeyFile: "/var/run/kubernetes/server.key",
|
|
||||||
CAFile: "/var/run/kubernetes/caserver.crt",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Audit: &apiserveroptions.AuditOptions{
|
|
||||||
LogOptions: apiserveroptions.AuditLogOptions{
|
|
||||||
Path: "/var/log",
|
|
||||||
MaxAge: 11,
|
|
||||||
MaxBackups: 12,
|
|
||||||
MaxSize: 13,
|
|
||||||
Format: "json",
|
|
||||||
BatchOptions: apiserveroptions.AuditBatchOptions{
|
|
||||||
Mode: "blocking",
|
|
||||||
BatchConfig: auditbuffered.BatchConfig{
|
|
||||||
BufferSize: 46,
|
|
||||||
MaxBatchSize: 47,
|
|
||||||
MaxBatchWait: 48 * time.Second,
|
|
||||||
ThrottleEnable: true,
|
|
||||||
ThrottleQPS: 49.5,
|
|
||||||
ThrottleBurst: 50,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
TruncateOptions: apiserveroptions.AuditTruncateOptions{
|
DefaultStorageMediaType: "application/vnd.kubernetes.protobuf",
|
||||||
Enabled: true,
|
DeleteCollectionWorkers: 1,
|
||||||
TruncateConfig: audittruncate.Config{
|
EnableGarbageCollection: true,
|
||||||
MaxBatchSize: 45,
|
EnableWatchCache: true,
|
||||||
MaxEventSize: 44,
|
DefaultWatchCacheSize: 100,
|
||||||
},
|
},
|
||||||
|
SecureServing: (&apiserveroptions.SecureServingOptions{
|
||||||
|
BindAddress: netutils.ParseIPSloppy("192.168.10.20"),
|
||||||
|
BindPort: 6443,
|
||||||
|
ServerCert: apiserveroptions.GeneratableKeyCert{
|
||||||
|
CertDirectory: "/var/run/kubernetes",
|
||||||
|
PairName: "apiserver",
|
||||||
},
|
},
|
||||||
GroupVersionString: "audit.k8s.io/v1",
|
HTTP2MaxStreamsPerConnection: 42,
|
||||||
},
|
Required: true,
|
||||||
WebhookOptions: apiserveroptions.AuditWebhookOptions{
|
}).WithLoopback(),
|
||||||
ConfigFile: "/webhook-config",
|
EventTTL: 1 * time.Hour,
|
||||||
BatchOptions: apiserveroptions.AuditBatchOptions{
|
Audit: &apiserveroptions.AuditOptions{
|
||||||
Mode: "blocking",
|
LogOptions: apiserveroptions.AuditLogOptions{
|
||||||
BatchConfig: auditbuffered.BatchConfig{
|
Path: "/var/log",
|
||||||
BufferSize: 42,
|
MaxAge: 11,
|
||||||
MaxBatchSize: 43,
|
MaxBackups: 12,
|
||||||
MaxBatchWait: 1 * time.Second,
|
MaxSize: 13,
|
||||||
ThrottleEnable: false,
|
Format: "json",
|
||||||
ThrottleQPS: 43.5,
|
BatchOptions: apiserveroptions.AuditBatchOptions{
|
||||||
ThrottleBurst: 44,
|
Mode: "blocking",
|
||||||
AsyncDelegate: true,
|
BatchConfig: auditbuffered.BatchConfig{
|
||||||
|
BufferSize: 46,
|
||||||
|
MaxBatchSize: 47,
|
||||||
|
MaxBatchWait: 48 * time.Second,
|
||||||
|
ThrottleEnable: true,
|
||||||
|
ThrottleQPS: 49.5,
|
||||||
|
ThrottleBurst: 50,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
TruncateOptions: apiserveroptions.AuditTruncateOptions{
|
||||||
TruncateOptions: apiserveroptions.AuditTruncateOptions{
|
Enabled: true,
|
||||||
Enabled: true,
|
TruncateConfig: audittruncate.Config{
|
||||||
TruncateConfig: audittruncate.Config{
|
MaxBatchSize: 45,
|
||||||
MaxBatchSize: 43,
|
MaxEventSize: 44,
|
||||||
MaxEventSize: 42,
|
},
|
||||||
},
|
},
|
||||||
|
GroupVersionString: "audit.k8s.io/v1",
|
||||||
},
|
},
|
||||||
InitialBackoff: 2 * time.Second,
|
WebhookOptions: apiserveroptions.AuditWebhookOptions{
|
||||||
GroupVersionString: "audit.k8s.io/v1",
|
ConfigFile: "/webhook-config",
|
||||||
|
BatchOptions: apiserveroptions.AuditBatchOptions{
|
||||||
|
Mode: "blocking",
|
||||||
|
BatchConfig: auditbuffered.BatchConfig{
|
||||||
|
BufferSize: 42,
|
||||||
|
MaxBatchSize: 43,
|
||||||
|
MaxBatchWait: 1 * time.Second,
|
||||||
|
ThrottleEnable: false,
|
||||||
|
ThrottleQPS: 43.5,
|
||||||
|
ThrottleBurst: 44,
|
||||||
|
AsyncDelegate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TruncateOptions: apiserveroptions.AuditTruncateOptions{
|
||||||
|
Enabled: true,
|
||||||
|
TruncateConfig: audittruncate.Config{
|
||||||
|
MaxBatchSize: 43,
|
||||||
|
MaxEventSize: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InitialBackoff: 2 * time.Second,
|
||||||
|
GroupVersionString: "audit.k8s.io/v1",
|
||||||
|
},
|
||||||
|
PolicyFile: "/policy",
|
||||||
},
|
},
|
||||||
PolicyFile: "/policy",
|
Features: &apiserveroptions.FeatureOptions{
|
||||||
|
EnableProfiling: true,
|
||||||
|
EnableContentionProfiling: true,
|
||||||
|
},
|
||||||
|
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
|
||||||
|
Anonymous: &kubeoptions.AnonymousAuthenticationOptions{
|
||||||
|
Allow: false,
|
||||||
|
},
|
||||||
|
ClientCert: &apiserveroptions.ClientCertAuthenticationOptions{
|
||||||
|
ClientCA: "/client-ca",
|
||||||
|
},
|
||||||
|
WebHook: &kubeoptions.WebHookAuthenticationOptions{
|
||||||
|
CacheTTL: 180000000000,
|
||||||
|
ConfigFile: "/token-webhook-config",
|
||||||
|
Version: "v1beta1",
|
||||||
|
RetryBackoff: apiserveroptions.DefaultAuthWebhookRetryBackoff(),
|
||||||
|
},
|
||||||
|
BootstrapToken: &kubeoptions.BootstrapTokenAuthenticationOptions{},
|
||||||
|
OIDC: &kubeoptions.OIDCAuthenticationOptions{
|
||||||
|
UsernameClaim: "sub",
|
||||||
|
SigningAlgs: []string{"RS256"},
|
||||||
|
},
|
||||||
|
RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{},
|
||||||
|
ServiceAccounts: &kubeoptions.ServiceAccountAuthenticationOptions{
|
||||||
|
Lookup: true,
|
||||||
|
ExtendExpiration: true,
|
||||||
|
},
|
||||||
|
TokenFile: &kubeoptions.TokenFileAuthenticationOptions{},
|
||||||
|
TokenSuccessCacheTTL: 10 * time.Second,
|
||||||
|
TokenFailureCacheTTL: 0,
|
||||||
|
},
|
||||||
|
Authorization: &kubeoptions.BuiltInAuthorizationOptions{
|
||||||
|
Modes: []string{"AlwaysDeny", "RBAC"},
|
||||||
|
PolicyFile: "/policy",
|
||||||
|
WebhookConfigFile: "/webhook-config",
|
||||||
|
WebhookCacheAuthorizedTTL: 180000000000,
|
||||||
|
WebhookCacheUnauthorizedTTL: 60000000000,
|
||||||
|
WebhookVersion: "v1beta1",
|
||||||
|
WebhookRetryBackoff: apiserveroptions.DefaultAuthWebhookRetryBackoff(),
|
||||||
|
},
|
||||||
|
APIEnablement: &apiserveroptions.APIEnablementOptions{
|
||||||
|
RuntimeConfig: cliflag.ConfigurationMap{},
|
||||||
|
},
|
||||||
|
EgressSelector: &apiserveroptions.EgressSelectorOptions{
|
||||||
|
ConfigFile: "/var/run/kubernetes/egress-selector/connectivity.yaml",
|
||||||
|
},
|
||||||
|
EnableLogsHandler: false,
|
||||||
|
EnableAggregatorRouting: true,
|
||||||
|
ProxyClientKeyFile: "/var/run/kubernetes/proxy.key",
|
||||||
|
ProxyClientCertFile: "/var/run/kubernetes/proxy.crt",
|
||||||
|
Metrics: &metrics.Options{},
|
||||||
|
Logs: logs.NewOptions(),
|
||||||
|
Traces: &apiserveroptions.TracingOptions{
|
||||||
|
ConfigFile: "/var/run/kubernetes/tracing_config.yaml",
|
||||||
|
},
|
||||||
|
AggregatorRejectForwardingRedirects: true,
|
||||||
},
|
},
|
||||||
Features: &apiserveroptions.FeatureOptions{
|
|
||||||
EnableProfiling: true,
|
Extra: Extra{
|
||||||
EnableContentionProfiling: true,
|
ServiceNodePortRange: kubeoptions.DefaultServiceNodePortRange,
|
||||||
},
|
ServiceClusterIPRanges: (&net.IPNet{IP: netutils.ParseIPSloppy("192.168.128.0"), Mask: net.CIDRMask(17, 32)}).String(),
|
||||||
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
|
EndpointReconcilerType: string(reconcilers.LeaseEndpointReconcilerType),
|
||||||
Anonymous: &kubeoptions.AnonymousAuthenticationOptions{
|
AllowPrivileged: false,
|
||||||
Allow: false,
|
KubeletConfig: kubeletclient.KubeletClientConfig{
|
||||||
|
Port: 10250,
|
||||||
|
ReadOnlyPort: 10255,
|
||||||
|
PreferredAddressTypes: []string{
|
||||||
|
string(kapi.NodeHostName),
|
||||||
|
string(kapi.NodeInternalDNS),
|
||||||
|
string(kapi.NodeInternalIP),
|
||||||
|
string(kapi.NodeExternalDNS),
|
||||||
|
string(kapi.NodeExternalIP),
|
||||||
|
},
|
||||||
|
HTTPTimeout: time.Duration(5) * time.Second,
|
||||||
|
TLSClientConfig: kubeletclient.KubeletTLSConfig{
|
||||||
|
CertFile: "/var/run/kubernetes/ceserver.crt",
|
||||||
|
KeyFile: "/var/run/kubernetes/server.key",
|
||||||
|
CAFile: "/var/run/kubernetes/caserver.crt",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
ClientCert: &apiserveroptions.ClientCertAuthenticationOptions{
|
|
||||||
ClientCA: "/client-ca",
|
|
||||||
},
|
|
||||||
WebHook: &kubeoptions.WebHookAuthenticationOptions{
|
|
||||||
CacheTTL: 180000000000,
|
|
||||||
ConfigFile: "/token-webhook-config",
|
|
||||||
Version: "v1beta1",
|
|
||||||
RetryBackoff: apiserveroptions.DefaultAuthWebhookRetryBackoff(),
|
|
||||||
},
|
|
||||||
BootstrapToken: &kubeoptions.BootstrapTokenAuthenticationOptions{},
|
|
||||||
OIDC: &kubeoptions.OIDCAuthenticationOptions{
|
|
||||||
UsernameClaim: "sub",
|
|
||||||
SigningAlgs: []string{"RS256"},
|
|
||||||
},
|
|
||||||
RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{},
|
|
||||||
ServiceAccounts: &kubeoptions.ServiceAccountAuthenticationOptions{
|
|
||||||
Lookup: true,
|
|
||||||
ExtendExpiration: true,
|
|
||||||
},
|
|
||||||
TokenFile: &kubeoptions.TokenFileAuthenticationOptions{},
|
|
||||||
TokenSuccessCacheTTL: 10 * time.Second,
|
|
||||||
TokenFailureCacheTTL: 0,
|
|
||||||
},
|
|
||||||
Authorization: &kubeoptions.BuiltInAuthorizationOptions{
|
|
||||||
Modes: []string{"AlwaysDeny", "RBAC"},
|
|
||||||
PolicyFile: "/policy",
|
|
||||||
WebhookConfigFile: "/webhook-config",
|
|
||||||
WebhookCacheAuthorizedTTL: 180000000000,
|
|
||||||
WebhookCacheUnauthorizedTTL: 60000000000,
|
|
||||||
WebhookVersion: "v1beta1",
|
|
||||||
WebhookRetryBackoff: apiserveroptions.DefaultAuthWebhookRetryBackoff(),
|
|
||||||
},
|
},
|
||||||
CloudProvider: &kubeoptions.CloudProviderOptions{
|
CloudProvider: &kubeoptions.CloudProviderOptions{
|
||||||
CloudConfigFile: "/cloud-config",
|
CloudConfigFile: "/cloud-config",
|
||||||
CloudProvider: "azure",
|
CloudProvider: "azure",
|
||||||
},
|
},
|
||||||
APIEnablement: &apiserveroptions.APIEnablementOptions{
|
|
||||||
RuntimeConfig: cliflag.ConfigurationMap{},
|
|
||||||
},
|
|
||||||
EgressSelector: &apiserveroptions.EgressSelectorOptions{
|
|
||||||
ConfigFile: "/var/run/kubernetes/egress-selector/connectivity.yaml",
|
|
||||||
},
|
|
||||||
EnableLogsHandler: false,
|
|
||||||
EnableAggregatorRouting: true,
|
|
||||||
ProxyClientKeyFile: "/var/run/kubernetes/proxy.key",
|
|
||||||
ProxyClientCertFile: "/var/run/kubernetes/proxy.crt",
|
|
||||||
Metrics: &metrics.Options{},
|
|
||||||
Logs: logs.NewOptions(),
|
|
||||||
Traces: &apiserveroptions.TracingOptions{
|
|
||||||
ConfigFile: "/var/run/kubernetes/tracing_config.yaml",
|
|
||||||
},
|
|
||||||
AggregatorRejectForwardingRedirects: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !reflect.DeepEqual(expected, s) {
|
if !reflect.DeepEqual(expected, s) {
|
||||||
|
@ -22,11 +22,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
|
|
||||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
|
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
)
|
)
|
||||||
@ -106,64 +102,13 @@ func validateServiceNodePort(options *ServerRunOptions) []error {
|
|||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateTokenRequest(options *ServerRunOptions) []error {
|
|
||||||
var errs []error
|
|
||||||
|
|
||||||
enableAttempted := options.ServiceAccountSigningKeyFile != "" ||
|
|
||||||
(len(options.Authentication.ServiceAccounts.Issuers) != 0 && options.Authentication.ServiceAccounts.Issuers[0] != "") ||
|
|
||||||
len(options.Authentication.APIAudiences) != 0
|
|
||||||
|
|
||||||
enableSucceeded := options.ServiceAccountIssuer != nil
|
|
||||||
|
|
||||||
if !enableAttempted {
|
|
||||||
errs = append(errs, errors.New("--service-account-signing-key-file and --service-account-issuer are required flags"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if enableAttempted && !enableSucceeded {
|
|
||||||
errs = append(errs, errors.New("--service-account-signing-key-file, --service-account-issuer, and --api-audiences should be specified together"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateAPIPriorityAndFairness(options *ServerRunOptions) []error {
|
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIPriorityAndFairness) && options.GenericServerRunOptions.EnablePriorityAndFairness {
|
|
||||||
// If none of the following runtime config options are specified,
|
|
||||||
// APF is assumed to be turned on. The internal APF controller uses
|
|
||||||
// v1beta3 so it should be enabled.
|
|
||||||
enabledAPIString := options.APIEnablement.RuntimeConfig.String()
|
|
||||||
testConfigs := []string{"flowcontrol.apiserver.k8s.io/v1beta3", "api/beta", "api/all"} // in the order of precedence
|
|
||||||
for _, testConfig := range testConfigs {
|
|
||||||
if strings.Contains(enabledAPIString, fmt.Sprintf("%s=false", testConfig)) {
|
|
||||||
return []error{fmt.Errorf("--runtime-config=%s=false conflicts with --enable-priority-and-fairness=true and --feature-gates=APIPriorityAndFairness=true", testConfig)}
|
|
||||||
}
|
|
||||||
if strings.Contains(enabledAPIString, fmt.Sprintf("%s=true", testConfig)) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate checks ServerRunOptions and return a slice of found errs.
|
// Validate checks ServerRunOptions and return a slice of found errs.
|
||||||
func (s *ServerRunOptions) Validate() []error {
|
func (s *ServerRunOptions) Validate() []error {
|
||||||
var errs []error
|
var errs []error
|
||||||
if s.MasterCount <= 0 {
|
|
||||||
errs = append(errs, fmt.Errorf("--apiserver-count should be a positive number, but value '%d' provided", s.MasterCount))
|
errs = append(errs, s.Options.Validate()...)
|
||||||
}
|
|
||||||
errs = append(errs, s.Etcd.Validate()...)
|
|
||||||
errs = append(errs, validateClusterIPFlags(s)...)
|
errs = append(errs, validateClusterIPFlags(s)...)
|
||||||
errs = append(errs, validateServiceNodePort(s)...)
|
errs = append(errs, validateServiceNodePort(s)...)
|
||||||
errs = append(errs, validateAPIPriorityAndFairness(s)...)
|
|
||||||
errs = append(errs, s.SecureServing.Validate()...)
|
|
||||||
errs = append(errs, s.Authentication.Validate()...)
|
|
||||||
errs = append(errs, s.Authorization.Validate()...)
|
|
||||||
errs = append(errs, s.Audit.Validate()...)
|
|
||||||
errs = append(errs, s.Admission.Validate()...)
|
|
||||||
errs = append(errs, s.APIEnablement.Validate(legacyscheme.Scheme, apiextensionsapiserver.Scheme, aggregatorscheme.Scheme)...)
|
|
||||||
errs = append(errs, validateTokenRequest(s)...)
|
|
||||||
errs = append(errs, s.Metrics.Validate()...)
|
|
||||||
|
|
||||||
return errs
|
return errs
|
||||||
}
|
}
|
||||||
|
@ -18,17 +18,12 @@ package options
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||||
kubeapiserveradmission "k8s.io/apiserver/pkg/admission"
|
|
||||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||||
basemetrics "k8s.io/component-base/metrics"
|
|
||||||
"k8s.io/kubernetes/pkg/features"
|
"k8s.io/kubernetes/pkg/features"
|
||||||
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
|
||||||
netutils "k8s.io/utils/net"
|
netutils "k8s.io/utils/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -53,9 +48,11 @@ func makeOptionsWithCIDRs(serviceCIDR string, secondaryServiceCIDR string) *Serv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &ServerRunOptions{
|
return &ServerRunOptions{
|
||||||
ServiceClusterIPRanges: value,
|
Extra: Extra{
|
||||||
PrimaryServiceClusterIPRange: primaryCIDR,
|
ServiceClusterIPRanges: value,
|
||||||
SecondaryServiceClusterIPRange: secondaryCIDR,
|
PrimaryServiceClusterIPRange: primaryCIDR,
|
||||||
|
SecondaryServiceClusterIPRange: secondaryCIDR,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,9 +200,9 @@ func TestValidateServiceNodePort(t *testing.T) {
|
|||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
err := validateServiceNodePort(tc.options)
|
errs := validateServiceNodePort(tc.options)
|
||||||
if err != nil && !tc.expectErrors {
|
if errs != nil && !tc.expectErrors {
|
||||||
t.Errorf("expected no errors, error found %+v", err)
|
t.Errorf("expected no errors, error found %+v", errs)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -217,8 +214,10 @@ func makeOptionsWithPort(kubernetesServiceNodePort int, base int, size int) *Ser
|
|||||||
Size: size,
|
Size: size,
|
||||||
}
|
}
|
||||||
return &ServerRunOptions{
|
return &ServerRunOptions{
|
||||||
ServiceNodePortRange: portRange,
|
Extra: Extra{
|
||||||
KubernetesServiceNodePort: kubernetesServiceNodePort,
|
ServiceNodePortRange: portRange,
|
||||||
|
KubernetesServiceNodePort: kubernetesServiceNodePort,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -283,144 +282,3 @@ func TestValidateMaxCIDRRange(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateAPIPriorityAndFairness(t *testing.T) {
|
|
||||||
const conflict = "conflicts with --enable-priority-and-fairness=true and --feature-gates=APIPriorityAndFairness=true"
|
|
||||||
tests := []struct {
|
|
||||||
runtimeConfig string
|
|
||||||
errShouldContain string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
runtimeConfig: "api/all=false",
|
|
||||||
errShouldContain: conflict,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
runtimeConfig: "api/beta=false",
|
|
||||||
errShouldContain: conflict,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta1=false",
|
|
||||||
errShouldContain: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta2=false",
|
|
||||||
errShouldContain: "",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta3=false",
|
|
||||||
errShouldContain: conflict,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta3=true",
|
|
||||||
errShouldContain: "",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.runtimeConfig, func(t *testing.T) {
|
|
||||||
options := &ServerRunOptions{
|
|
||||||
GenericServerRunOptions: &genericoptions.ServerRunOptions{
|
|
||||||
EnablePriorityAndFairness: true,
|
|
||||||
},
|
|
||||||
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
|
||||||
}
|
|
||||||
options.APIEnablement.RuntimeConfig.Set(test.runtimeConfig)
|
|
||||||
|
|
||||||
var errMessageGot string
|
|
||||||
if errs := validateAPIPriorityAndFairness(options); len(errs) > 0 {
|
|
||||||
errMessageGot = errs[0].Error()
|
|
||||||
}
|
|
||||||
if !strings.Contains(errMessageGot, test.errShouldContain) {
|
|
||||||
t.Errorf("Expected error message to contain: %q, but got: %q", test.errShouldContain, errMessageGot)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestValidateServerRunOptions(t *testing.T) {
|
|
||||||
cidrOpts := makeOptionsWithCIDRs("10.0.0.0/16", "3000::/64")
|
|
||||||
nodePortOpts := makeOptionsWithPort(-1, 30065, 1)
|
|
||||||
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
options *ServerRunOptions
|
|
||||||
expectErrors bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "validate master count equal 0",
|
|
||||||
expectErrors: true,
|
|
||||||
options: &ServerRunOptions{
|
|
||||||
MasterCount: 0,
|
|
||||||
GenericServerRunOptions: &genericoptions.ServerRunOptions{},
|
|
||||||
Etcd: &genericoptions.EtcdOptions{},
|
|
||||||
SecureServing: &genericoptions.SecureServingOptionsWithLoopback{},
|
|
||||||
Audit: &genericoptions.AuditOptions{},
|
|
||||||
Admission: &kubeoptions.AdmissionOptions{
|
|
||||||
GenericAdmission: &genericoptions.AdmissionOptions{
|
|
||||||
EnablePlugins: []string{"foo"},
|
|
||||||
Plugins: kubeapiserveradmission.NewPlugins(),
|
|
||||||
},
|
|
||||||
PluginNames: []string{"foo"},
|
|
||||||
},
|
|
||||||
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
|
|
||||||
APIAudiences: []string{"bar"},
|
|
||||||
ServiceAccounts: &kubeoptions.ServiceAccountAuthenticationOptions{
|
|
||||||
Issuers: []string{"baz"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Authorization: &kubeoptions.BuiltInAuthorizationOptions{},
|
|
||||||
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
|
||||||
Metrics: &basemetrics.Options{},
|
|
||||||
ServiceClusterIPRanges: cidrOpts.ServiceClusterIPRanges,
|
|
||||||
PrimaryServiceClusterIPRange: cidrOpts.PrimaryServiceClusterIPRange,
|
|
||||||
SecondaryServiceClusterIPRange: cidrOpts.SecondaryServiceClusterIPRange,
|
|
||||||
ServiceNodePortRange: nodePortOpts.ServiceNodePortRange,
|
|
||||||
KubernetesServiceNodePort: nodePortOpts.KubernetesServiceNodePort,
|
|
||||||
ServiceAccountSigningKeyFile: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "validate token request enable not attempted",
|
|
||||||
expectErrors: true,
|
|
||||||
options: &ServerRunOptions{
|
|
||||||
MasterCount: 1,
|
|
||||||
GenericServerRunOptions: &genericoptions.ServerRunOptions{},
|
|
||||||
Etcd: &genericoptions.EtcdOptions{},
|
|
||||||
SecureServing: &genericoptions.SecureServingOptionsWithLoopback{},
|
|
||||||
Audit: &genericoptions.AuditOptions{},
|
|
||||||
Admission: &kubeoptions.AdmissionOptions{
|
|
||||||
GenericAdmission: &genericoptions.AdmissionOptions{
|
|
||||||
EnablePlugins: []string{""},
|
|
||||||
Plugins: kubeapiserveradmission.NewPlugins(),
|
|
||||||
},
|
|
||||||
PluginNames: []string{""},
|
|
||||||
},
|
|
||||||
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
|
|
||||||
ServiceAccounts: &kubeoptions.ServiceAccountAuthenticationOptions{},
|
|
||||||
},
|
|
||||||
Authorization: &kubeoptions.BuiltInAuthorizationOptions{},
|
|
||||||
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
|
||||||
Metrics: &basemetrics.Options{},
|
|
||||||
ServiceClusterIPRanges: cidrOpts.ServiceClusterIPRanges,
|
|
||||||
PrimaryServiceClusterIPRange: cidrOpts.PrimaryServiceClusterIPRange,
|
|
||||||
SecondaryServiceClusterIPRange: cidrOpts.SecondaryServiceClusterIPRange,
|
|
||||||
ServiceNodePortRange: nodePortOpts.ServiceNodePortRange,
|
|
||||||
KubernetesServiceNodePort: nodePortOpts.KubernetesServiceNodePort,
|
|
||||||
ServiceAccountSigningKeyFile: "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
errs := tc.options.Validate()
|
|
||||||
if len(errs) > 0 && !tc.expectErrors {
|
|
||||||
t.Errorf("expected no errors, errors found %+v", errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(errs) == 0 && tc.expectErrors {
|
|
||||||
t.Errorf("expected errors, no errors found")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
292
pkg/controlplane/apiserver/options/options.go
Normal file
292
pkg/controlplane/apiserver/options/options.go
Normal file
@ -0,0 +1,292 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 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 contains flags and options for initializing an apiserver
|
||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||||
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
|
"k8s.io/client-go/util/keyutil"
|
||||||
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
|
"k8s.io/component-base/logs"
|
||||||
|
logsapi "k8s.io/component-base/logs/api/v1"
|
||||||
|
"k8s.io/component-base/metrics"
|
||||||
|
"k8s.io/klog/v2"
|
||||||
|
"k8s.io/utils/integer"
|
||||||
|
netutil "k8s.io/utils/net"
|
||||||
|
|
||||||
|
_ "k8s.io/kubernetes/pkg/features"
|
||||||
|
kubeauthenticator "k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
|
||||||
|
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
||||||
|
"k8s.io/kubernetes/pkg/serviceaccount"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Options struct {
|
||||||
|
GenericServerRunOptions *genericoptions.ServerRunOptions
|
||||||
|
Etcd *genericoptions.EtcdOptions
|
||||||
|
SecureServing *genericoptions.SecureServingOptionsWithLoopback
|
||||||
|
Audit *genericoptions.AuditOptions
|
||||||
|
Features *genericoptions.FeatureOptions
|
||||||
|
Admission *kubeoptions.AdmissionOptions
|
||||||
|
Authentication *kubeoptions.BuiltInAuthenticationOptions
|
||||||
|
Authorization *kubeoptions.BuiltInAuthorizationOptions
|
||||||
|
APIEnablement *genericoptions.APIEnablementOptions
|
||||||
|
EgressSelector *genericoptions.EgressSelectorOptions
|
||||||
|
Metrics *metrics.Options
|
||||||
|
Logs *logs.Options
|
||||||
|
Traces *genericoptions.TracingOptions
|
||||||
|
|
||||||
|
EnableLogsHandler bool
|
||||||
|
EventTTL time.Duration
|
||||||
|
MaxConnectionBytesPerSec int64
|
||||||
|
|
||||||
|
ProxyClientCertFile string
|
||||||
|
ProxyClientKeyFile string
|
||||||
|
|
||||||
|
EnableAggregatorRouting bool
|
||||||
|
AggregatorRejectForwardingRedirects bool
|
||||||
|
|
||||||
|
MasterCount int
|
||||||
|
|
||||||
|
ServiceAccountSigningKeyFile string
|
||||||
|
ServiceAccountIssuer serviceaccount.TokenGenerator
|
||||||
|
ServiceAccountTokenMaxExpiration time.Duration
|
||||||
|
|
||||||
|
ShowHiddenMetricsForVersion string
|
||||||
|
}
|
||||||
|
|
||||||
|
// completedServerRunOptions is a private wrapper that enforces a call of Complete() before Run can be invoked.
|
||||||
|
type completedOptions struct {
|
||||||
|
Options
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompletedOptions struct {
|
||||||
|
// Embed a private pointer that cannot be instantiated outside of this package.
|
||||||
|
*completedOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewOptions creates a new ServerRunOptions object with default parameters
|
||||||
|
func NewOptions() *Options {
|
||||||
|
s := Options{
|
||||||
|
GenericServerRunOptions: genericoptions.NewServerRunOptions(),
|
||||||
|
Etcd: genericoptions.NewEtcdOptions(storagebackend.NewDefaultConfig(kubeoptions.DefaultEtcdPathPrefix, nil)),
|
||||||
|
SecureServing: kubeoptions.NewSecureServingOptions(),
|
||||||
|
Audit: genericoptions.NewAuditOptions(),
|
||||||
|
Features: genericoptions.NewFeatureOptions(),
|
||||||
|
Admission: kubeoptions.NewAdmissionOptions(),
|
||||||
|
Authentication: kubeoptions.NewBuiltInAuthenticationOptions().WithAll(),
|
||||||
|
Authorization: kubeoptions.NewBuiltInAuthorizationOptions(),
|
||||||
|
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
||||||
|
EgressSelector: genericoptions.NewEgressSelectorOptions(),
|
||||||
|
Metrics: metrics.NewOptions(),
|
||||||
|
Logs: logs.NewOptions(),
|
||||||
|
Traces: genericoptions.NewTracingOptions(),
|
||||||
|
|
||||||
|
EnableLogsHandler: true,
|
||||||
|
EventTTL: 1 * time.Hour,
|
||||||
|
MasterCount: 1,
|
||||||
|
AggregatorRejectForwardingRedirects: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overwrite the default for storage data format.
|
||||||
|
s.Etcd.DefaultStorageMediaType = "application/vnd.kubernetes.protobuf"
|
||||||
|
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Options) AddFlags(fss *cliflag.NamedFlagSets) {
|
||||||
|
// Add the generic flags.
|
||||||
|
s.GenericServerRunOptions.AddUniversalFlags(fss.FlagSet("generic"))
|
||||||
|
s.Etcd.AddFlags(fss.FlagSet("etcd"))
|
||||||
|
s.SecureServing.AddFlags(fss.FlagSet("secure serving"))
|
||||||
|
s.Audit.AddFlags(fss.FlagSet("auditing"))
|
||||||
|
s.Features.AddFlags(fss.FlagSet("features"))
|
||||||
|
s.Authentication.AddFlags(fss.FlagSet("authentication"))
|
||||||
|
s.Authorization.AddFlags(fss.FlagSet("authorization"))
|
||||||
|
s.APIEnablement.AddFlags(fss.FlagSet("API enablement"))
|
||||||
|
s.EgressSelector.AddFlags(fss.FlagSet("egress selector"))
|
||||||
|
s.Admission.AddFlags(fss.FlagSet("admission"))
|
||||||
|
s.Metrics.AddFlags(fss.FlagSet("metrics"))
|
||||||
|
logsapi.AddFlags(s.Logs, fss.FlagSet("logs"))
|
||||||
|
s.Traces.AddFlags(fss.FlagSet("traces"))
|
||||||
|
|
||||||
|
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||||
|
// arrange these text blocks sensibly. Grrr.
|
||||||
|
fs := fss.FlagSet("misc")
|
||||||
|
fs.DurationVar(&s.EventTTL, "event-ttl", s.EventTTL,
|
||||||
|
"Amount of time to retain events.")
|
||||||
|
|
||||||
|
fs.BoolVar(&s.EnableLogsHandler, "enable-logs-handler", s.EnableLogsHandler,
|
||||||
|
"If true, install a /logs handler for the apiserver logs.")
|
||||||
|
fs.MarkDeprecated("enable-logs-handler", "This flag will be removed in v1.19")
|
||||||
|
|
||||||
|
fs.Int64Var(&s.MaxConnectionBytesPerSec, "max-connection-bytes-per-sec", s.MaxConnectionBytesPerSec, ""+
|
||||||
|
"If non-zero, throttle each user connection to this number of bytes/sec. "+
|
||||||
|
"Currently only applies to long-running requests.")
|
||||||
|
|
||||||
|
fs.IntVar(&s.MasterCount, "apiserver-count", s.MasterCount,
|
||||||
|
"The number of apiservers running in the cluster, must be a positive number. (In use when --endpoint-reconciler-type=master-count is enabled.)")
|
||||||
|
fs.MarkDeprecated("apiserver-count", "apiserver-count is deprecated and will be removed in a future version.")
|
||||||
|
|
||||||
|
fs.StringVar(&s.ProxyClientCertFile, "proxy-client-cert-file", s.ProxyClientCertFile, ""+
|
||||||
|
"Client certificate used to prove the identity of the aggregator or kube-apiserver "+
|
||||||
|
"when it must call out during a request. This includes proxying requests to a user "+
|
||||||
|
"api-server and calling out to webhook admission plugins. It is expected that this "+
|
||||||
|
"cert includes a signature from the CA in the --requestheader-client-ca-file flag. "+
|
||||||
|
"That CA is published in the 'extension-apiserver-authentication' configmap in "+
|
||||||
|
"the kube-system namespace. Components receiving calls from kube-aggregator should "+
|
||||||
|
"use that CA to perform their half of the mutual TLS verification.")
|
||||||
|
fs.StringVar(&s.ProxyClientKeyFile, "proxy-client-key-file", s.ProxyClientKeyFile, ""+
|
||||||
|
"Private key for the client certificate used to prove the identity of the aggregator or kube-apiserver "+
|
||||||
|
"when it must call out during a request. This includes proxying requests to a user "+
|
||||||
|
"api-server and calling out to webhook admission plugins.")
|
||||||
|
|
||||||
|
fs.BoolVar(&s.EnableAggregatorRouting, "enable-aggregator-routing", s.EnableAggregatorRouting,
|
||||||
|
"Turns on aggregator routing requests to endpoints IP rather than cluster IP.")
|
||||||
|
|
||||||
|
fs.BoolVar(&s.AggregatorRejectForwardingRedirects, "aggregator-reject-forwarding-redirect", s.AggregatorRejectForwardingRedirects,
|
||||||
|
"Aggregator reject forwarding redirect response back to client.")
|
||||||
|
|
||||||
|
fs.StringVar(&s.ServiceAccountSigningKeyFile, "service-account-signing-key-file", s.ServiceAccountSigningKeyFile, ""+
|
||||||
|
"Path to the file that contains the current private key of the service account token issuer. The issuer will sign issued ID tokens with this private key.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Options) Complete(alternateDNS []string, alternateIPs []net.IP) (CompletedOptions, error) {
|
||||||
|
if o == nil {
|
||||||
|
return CompletedOptions{completedOptions: &completedOptions{}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
completed := completedOptions{
|
||||||
|
Options: *o,
|
||||||
|
}
|
||||||
|
|
||||||
|
// set defaults
|
||||||
|
if err := completed.GenericServerRunOptions.DefaultAdvertiseAddress(completed.SecureServing.SecureServingOptions); err != nil {
|
||||||
|
return CompletedOptions{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := completed.SecureServing.MaybeDefaultWithSelfSignedCerts(completed.GenericServerRunOptions.AdvertiseAddress.String(), alternateDNS, alternateIPs); err != nil {
|
||||||
|
return CompletedOptions{}, fmt.Errorf("error creating self-signed certificates: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(completed.GenericServerRunOptions.ExternalHost) == 0 {
|
||||||
|
if len(completed.GenericServerRunOptions.AdvertiseAddress) > 0 {
|
||||||
|
completed.GenericServerRunOptions.ExternalHost = completed.GenericServerRunOptions.AdvertiseAddress.String()
|
||||||
|
} else {
|
||||||
|
hostname, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
return CompletedOptions{}, fmt.Errorf("error finding host name: %v", err)
|
||||||
|
}
|
||||||
|
completed.GenericServerRunOptions.ExternalHost = hostname
|
||||||
|
}
|
||||||
|
klog.Infof("external host was not specified, using %v", completed.GenericServerRunOptions.ExternalHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
completed.Authentication.ApplyAuthorization(completed.Authorization)
|
||||||
|
|
||||||
|
// Use (ServiceAccountSigningKeyFile != "") as a proxy to the user enabling
|
||||||
|
// TokenRequest functionality. This defaulting was convenient, but messed up
|
||||||
|
// a lot of people when they rotated their serving cert with no idea it was
|
||||||
|
// connected to their service account keys. We are taking this opportunity to
|
||||||
|
// remove this problematic defaulting.
|
||||||
|
if completed.ServiceAccountSigningKeyFile == "" {
|
||||||
|
// Default to the private server key for service account token signing
|
||||||
|
if len(completed.Authentication.ServiceAccounts.KeyFiles) == 0 && completed.SecureServing.ServerCert.CertKey.KeyFile != "" {
|
||||||
|
if kubeauthenticator.IsValidServiceAccountKeyFile(completed.SecureServing.ServerCert.CertKey.KeyFile) {
|
||||||
|
completed.Authentication.ServiceAccounts.KeyFiles = []string{completed.SecureServing.ServerCert.CertKey.KeyFile}
|
||||||
|
} else {
|
||||||
|
klog.Warning("No TLS key provided, service account token authentication disabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if completed.ServiceAccountSigningKeyFile != "" && len(completed.Authentication.ServiceAccounts.Issuers) != 0 && completed.Authentication.ServiceAccounts.Issuers[0] != "" {
|
||||||
|
sk, err := keyutil.PrivateKeyFromFile(completed.ServiceAccountSigningKeyFile)
|
||||||
|
if err != nil {
|
||||||
|
return CompletedOptions{}, fmt.Errorf("failed to parse service-account-issuer-key-file: %v", err)
|
||||||
|
}
|
||||||
|
if completed.Authentication.ServiceAccounts.MaxExpiration != 0 {
|
||||||
|
lowBound := time.Hour
|
||||||
|
upBound := time.Duration(1<<32) * time.Second
|
||||||
|
if completed.Authentication.ServiceAccounts.MaxExpiration < lowBound ||
|
||||||
|
completed.Authentication.ServiceAccounts.MaxExpiration > upBound {
|
||||||
|
return CompletedOptions{}, fmt.Errorf("the service-account-max-token-expiration must be between 1 hour and 2^32 seconds")
|
||||||
|
}
|
||||||
|
if completed.Authentication.ServiceAccounts.ExtendExpiration {
|
||||||
|
if completed.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.WarnOnlyBoundTokenExpirationSeconds*time.Second {
|
||||||
|
klog.Warningf("service-account-extend-token-expiration is true, in order to correctly trigger safe transition logic, service-account-max-token-expiration must be set longer than %d seconds (currently %s)", serviceaccount.WarnOnlyBoundTokenExpirationSeconds, completed.Authentication.ServiceAccounts.MaxExpiration)
|
||||||
|
}
|
||||||
|
if completed.Authentication.ServiceAccounts.MaxExpiration < serviceaccount.ExpirationExtensionSeconds*time.Second {
|
||||||
|
klog.Warningf("service-account-extend-token-expiration is true, enabling tokens valid up to %d seconds, which is longer than service-account-max-token-expiration set to %s seconds", serviceaccount.ExpirationExtensionSeconds, completed.Authentication.ServiceAccounts.MaxExpiration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completed.ServiceAccountIssuer, err = serviceaccount.JWTTokenGenerator(completed.Authentication.ServiceAccounts.Issuers[0], sk)
|
||||||
|
if err != nil {
|
||||||
|
return CompletedOptions{}, fmt.Errorf("failed to build token generator: %v", err)
|
||||||
|
}
|
||||||
|
completed.ServiceAccountTokenMaxExpiration = completed.Authentication.ServiceAccounts.MaxExpiration
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, value := range completed.APIEnablement.RuntimeConfig {
|
||||||
|
if key == "v1" || strings.HasPrefix(key, "v1/") ||
|
||||||
|
key == "api/v1" || strings.HasPrefix(key, "api/v1/") {
|
||||||
|
delete(completed.APIEnablement.RuntimeConfig, key)
|
||||||
|
completed.APIEnablement.RuntimeConfig["/v1"] = value
|
||||||
|
}
|
||||||
|
if key == "api/legacy" {
|
||||||
|
delete(completed.APIEnablement.RuntimeConfig, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CompletedOptions{
|
||||||
|
completedOptions: &completed,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceIPRange checks if the serviceClusterIPRange flag is nil, raising a warning if so and
|
||||||
|
// setting service ip range to the default value in kubeoptions.DefaultServiceIPCIDR
|
||||||
|
// for now until the default is removed per the deprecation timeline guidelines.
|
||||||
|
// Returns service ip range, api server service IP, and an error
|
||||||
|
func ServiceIPRange(passedServiceClusterIPRange net.IPNet) (net.IPNet, net.IP, error) {
|
||||||
|
serviceClusterIPRange := passedServiceClusterIPRange
|
||||||
|
if passedServiceClusterIPRange.IP == nil {
|
||||||
|
klog.Warningf("No CIDR for service cluster IPs specified. Default value which was %s is deprecated and will be removed in future releases. Please specify it using --service-cluster-ip-range on kube-apiserver.", kubeoptions.DefaultServiceIPCIDR.String())
|
||||||
|
serviceClusterIPRange = kubeoptions.DefaultServiceIPCIDR
|
||||||
|
}
|
||||||
|
|
||||||
|
size := integer.Int64Min(netutil.RangeSize(&serviceClusterIPRange), 1<<16)
|
||||||
|
if size < 8 {
|
||||||
|
return net.IPNet{}, net.IP{}, fmt.Errorf("the service cluster IP range must be at least %d IP addresses", 8)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the first valid IP from ServiceClusterIPRange to use as the GenericAPIServer service IP.
|
||||||
|
apiServerServiceIP, err := netutil.GetIndexedIP(&serviceClusterIPRange, 1)
|
||||||
|
if err != nil {
|
||||||
|
return net.IPNet{}, net.IP{}, err
|
||||||
|
}
|
||||||
|
klog.V(4).Infof("Setting service IP to %q (read-write).", apiServerServiceIP)
|
||||||
|
|
||||||
|
return serviceClusterIPRange, apiServerServiceIP, nil
|
||||||
|
}
|
291
pkg/controlplane/apiserver/options/options_test.go
Normal file
291
pkg/controlplane/apiserver/options/options_test.go
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 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 (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||||||
|
"github.com/spf13/pflag"
|
||||||
|
oteltrace "go.opentelemetry.io/otel/trace"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
apiserveroptions "k8s.io/apiserver/pkg/server/options"
|
||||||
|
"k8s.io/apiserver/pkg/storage/etcd3"
|
||||||
|
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||||
|
auditbuffered "k8s.io/apiserver/plugin/pkg/audit/buffered"
|
||||||
|
audittruncate "k8s.io/apiserver/plugin/pkg/audit/truncate"
|
||||||
|
cliflag "k8s.io/component-base/cli/flag"
|
||||||
|
"k8s.io/component-base/logs"
|
||||||
|
"k8s.io/component-base/metrics"
|
||||||
|
netutils "k8s.io/utils/net"
|
||||||
|
|
||||||
|
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAddFlags(t *testing.T) {
|
||||||
|
fs := pflag.NewFlagSet("addflagstest", pflag.PanicOnError)
|
||||||
|
s := NewOptions()
|
||||||
|
var fss cliflag.NamedFlagSets
|
||||||
|
s.AddFlags(&fss)
|
||||||
|
for _, f := range fss.FlagSets {
|
||||||
|
fs.AddFlagSet(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"--enable-admission-plugins=AlwaysDeny",
|
||||||
|
"--admission-control-config-file=/admission-control-config",
|
||||||
|
"--advertise-address=192.168.10.10",
|
||||||
|
"--anonymous-auth=false",
|
||||||
|
"--apiserver-count=5",
|
||||||
|
"--audit-log-maxage=11",
|
||||||
|
"--audit-log-maxbackup=12",
|
||||||
|
"--audit-log-maxsize=13",
|
||||||
|
"--audit-log-path=/var/log",
|
||||||
|
"--audit-log-mode=blocking",
|
||||||
|
"--audit-log-batch-buffer-size=46",
|
||||||
|
"--audit-log-batch-max-size=47",
|
||||||
|
"--audit-log-batch-max-wait=48s",
|
||||||
|
"--audit-log-batch-throttle-enable=true",
|
||||||
|
"--audit-log-batch-throttle-qps=49.5",
|
||||||
|
"--audit-log-batch-throttle-burst=50",
|
||||||
|
"--audit-log-truncate-enabled=true",
|
||||||
|
"--audit-log-truncate-max-batch-size=45",
|
||||||
|
"--audit-log-truncate-max-event-size=44",
|
||||||
|
"--audit-log-version=audit.k8s.io/v1",
|
||||||
|
"--audit-policy-file=/policy",
|
||||||
|
"--audit-webhook-config-file=/webhook-config",
|
||||||
|
"--audit-webhook-mode=blocking",
|
||||||
|
"--audit-webhook-batch-buffer-size=42",
|
||||||
|
"--audit-webhook-batch-max-size=43",
|
||||||
|
"--audit-webhook-batch-max-wait=1s",
|
||||||
|
"--audit-webhook-batch-throttle-enable=false",
|
||||||
|
"--audit-webhook-batch-throttle-qps=43.5",
|
||||||
|
"--audit-webhook-batch-throttle-burst=44",
|
||||||
|
"--audit-webhook-truncate-enabled=true",
|
||||||
|
"--audit-webhook-truncate-max-batch-size=43",
|
||||||
|
"--audit-webhook-truncate-max-event-size=42",
|
||||||
|
"--audit-webhook-initial-backoff=2s",
|
||||||
|
"--audit-webhook-version=audit.k8s.io/v1",
|
||||||
|
"--authentication-token-webhook-cache-ttl=3m",
|
||||||
|
"--authentication-token-webhook-config-file=/token-webhook-config",
|
||||||
|
"--authorization-mode=AlwaysDeny,RBAC",
|
||||||
|
"--authorization-policy-file=/policy",
|
||||||
|
"--authorization-webhook-cache-authorized-ttl=3m",
|
||||||
|
"--authorization-webhook-cache-unauthorized-ttl=1m",
|
||||||
|
"--authorization-webhook-config-file=/webhook-config",
|
||||||
|
"--bind-address=192.168.10.20",
|
||||||
|
"--client-ca-file=/client-ca",
|
||||||
|
"--cors-allowed-origins=10.10.10.100,10.10.10.200",
|
||||||
|
"--contention-profiling=true",
|
||||||
|
"--egress-selector-config-file=/var/run/kubernetes/egress-selector/connectivity.yaml",
|
||||||
|
"--enable-aggregator-routing=true",
|
||||||
|
"--enable-priority-and-fairness=false",
|
||||||
|
"--enable-logs-handler=false",
|
||||||
|
"--etcd-keyfile=/var/run/kubernetes/etcd.key",
|
||||||
|
"--etcd-certfile=/var/run/kubernetes/etcdce.crt",
|
||||||
|
"--etcd-cafile=/var/run/kubernetes/etcdca.crt",
|
||||||
|
"--http2-max-streams-per-connection=42",
|
||||||
|
"--tracing-config-file=/var/run/kubernetes/tracing_config.yaml",
|
||||||
|
"--proxy-client-cert-file=/var/run/kubernetes/proxy.crt",
|
||||||
|
"--proxy-client-key-file=/var/run/kubernetes/proxy.key",
|
||||||
|
"--request-timeout=2m",
|
||||||
|
"--storage-backend=etcd3",
|
||||||
|
"--lease-reuse-duration-seconds=100",
|
||||||
|
}
|
||||||
|
fs.Parse(args)
|
||||||
|
|
||||||
|
// This is a snapshot of expected options parsed by args.
|
||||||
|
expected := &Options{
|
||||||
|
MasterCount: 5,
|
||||||
|
GenericServerRunOptions: &apiserveroptions.ServerRunOptions{
|
||||||
|
AdvertiseAddress: netutils.ParseIPSloppy("192.168.10.10"),
|
||||||
|
CorsAllowedOriginList: []string{"10.10.10.100", "10.10.10.200"},
|
||||||
|
MaxRequestsInFlight: 400,
|
||||||
|
MaxMutatingRequestsInFlight: 200,
|
||||||
|
RequestTimeout: time.Duration(2) * time.Minute,
|
||||||
|
MinRequestTimeout: 1800,
|
||||||
|
JSONPatchMaxCopyBytes: int64(3 * 1024 * 1024),
|
||||||
|
MaxRequestBodyBytes: int64(3 * 1024 * 1024),
|
||||||
|
},
|
||||||
|
Admission: &kubeoptions.AdmissionOptions{
|
||||||
|
GenericAdmission: &apiserveroptions.AdmissionOptions{
|
||||||
|
RecommendedPluginOrder: s.Admission.GenericAdmission.RecommendedPluginOrder,
|
||||||
|
DefaultOffPlugins: s.Admission.GenericAdmission.DefaultOffPlugins,
|
||||||
|
EnablePlugins: []string{"AlwaysDeny"},
|
||||||
|
ConfigFile: "/admission-control-config",
|
||||||
|
Plugins: s.Admission.GenericAdmission.Plugins,
|
||||||
|
Decorators: s.Admission.GenericAdmission.Decorators,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Etcd: &apiserveroptions.EtcdOptions{
|
||||||
|
StorageConfig: storagebackend.Config{
|
||||||
|
Type: "etcd3",
|
||||||
|
Transport: storagebackend.TransportConfig{
|
||||||
|
ServerList: nil,
|
||||||
|
KeyFile: "/var/run/kubernetes/etcd.key",
|
||||||
|
TrustedCAFile: "/var/run/kubernetes/etcdca.crt",
|
||||||
|
CertFile: "/var/run/kubernetes/etcdce.crt",
|
||||||
|
TracerProvider: oteltrace.NewNoopTracerProvider(),
|
||||||
|
},
|
||||||
|
Paging: true,
|
||||||
|
Prefix: "/registry",
|
||||||
|
CompactionInterval: storagebackend.DefaultCompactInterval,
|
||||||
|
CountMetricPollPeriod: time.Minute,
|
||||||
|
DBMetricPollInterval: storagebackend.DefaultDBMetricPollInterval,
|
||||||
|
HealthcheckTimeout: storagebackend.DefaultHealthcheckTimeout,
|
||||||
|
ReadycheckTimeout: storagebackend.DefaultReadinessTimeout,
|
||||||
|
LeaseManagerConfig: etcd3.LeaseManagerConfig{
|
||||||
|
ReuseDurationSeconds: 100,
|
||||||
|
MaxObjectCount: 1000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultStorageMediaType: "application/vnd.kubernetes.protobuf",
|
||||||
|
DeleteCollectionWorkers: 1,
|
||||||
|
EnableGarbageCollection: true,
|
||||||
|
EnableWatchCache: true,
|
||||||
|
DefaultWatchCacheSize: 100,
|
||||||
|
},
|
||||||
|
SecureServing: (&apiserveroptions.SecureServingOptions{
|
||||||
|
BindAddress: netutils.ParseIPSloppy("192.168.10.20"),
|
||||||
|
BindPort: 6443,
|
||||||
|
ServerCert: apiserveroptions.GeneratableKeyCert{
|
||||||
|
CertDirectory: "/var/run/kubernetes",
|
||||||
|
PairName: "apiserver",
|
||||||
|
},
|
||||||
|
HTTP2MaxStreamsPerConnection: 42,
|
||||||
|
Required: true,
|
||||||
|
}).WithLoopback(),
|
||||||
|
EventTTL: 1 * time.Hour,
|
||||||
|
Audit: &apiserveroptions.AuditOptions{
|
||||||
|
LogOptions: apiserveroptions.AuditLogOptions{
|
||||||
|
Path: "/var/log",
|
||||||
|
MaxAge: 11,
|
||||||
|
MaxBackups: 12,
|
||||||
|
MaxSize: 13,
|
||||||
|
Format: "json",
|
||||||
|
BatchOptions: apiserveroptions.AuditBatchOptions{
|
||||||
|
Mode: "blocking",
|
||||||
|
BatchConfig: auditbuffered.BatchConfig{
|
||||||
|
BufferSize: 46,
|
||||||
|
MaxBatchSize: 47,
|
||||||
|
MaxBatchWait: 48 * time.Second,
|
||||||
|
ThrottleEnable: true,
|
||||||
|
ThrottleQPS: 49.5,
|
||||||
|
ThrottleBurst: 50,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TruncateOptions: apiserveroptions.AuditTruncateOptions{
|
||||||
|
Enabled: true,
|
||||||
|
TruncateConfig: audittruncate.Config{
|
||||||
|
MaxBatchSize: 45,
|
||||||
|
MaxEventSize: 44,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
GroupVersionString: "audit.k8s.io/v1",
|
||||||
|
},
|
||||||
|
WebhookOptions: apiserveroptions.AuditWebhookOptions{
|
||||||
|
ConfigFile: "/webhook-config",
|
||||||
|
BatchOptions: apiserveroptions.AuditBatchOptions{
|
||||||
|
Mode: "blocking",
|
||||||
|
BatchConfig: auditbuffered.BatchConfig{
|
||||||
|
BufferSize: 42,
|
||||||
|
MaxBatchSize: 43,
|
||||||
|
MaxBatchWait: 1 * time.Second,
|
||||||
|
ThrottleEnable: false,
|
||||||
|
ThrottleQPS: 43.5,
|
||||||
|
ThrottleBurst: 44,
|
||||||
|
AsyncDelegate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TruncateOptions: apiserveroptions.AuditTruncateOptions{
|
||||||
|
Enabled: true,
|
||||||
|
TruncateConfig: audittruncate.Config{
|
||||||
|
MaxBatchSize: 43,
|
||||||
|
MaxEventSize: 42,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
InitialBackoff: 2 * time.Second,
|
||||||
|
GroupVersionString: "audit.k8s.io/v1",
|
||||||
|
},
|
||||||
|
PolicyFile: "/policy",
|
||||||
|
},
|
||||||
|
Features: &apiserveroptions.FeatureOptions{
|
||||||
|
EnableProfiling: true,
|
||||||
|
EnableContentionProfiling: true,
|
||||||
|
},
|
||||||
|
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
|
||||||
|
Anonymous: &kubeoptions.AnonymousAuthenticationOptions{
|
||||||
|
Allow: false,
|
||||||
|
},
|
||||||
|
ClientCert: &apiserveroptions.ClientCertAuthenticationOptions{
|
||||||
|
ClientCA: "/client-ca",
|
||||||
|
},
|
||||||
|
WebHook: &kubeoptions.WebHookAuthenticationOptions{
|
||||||
|
CacheTTL: 180000000000,
|
||||||
|
ConfigFile: "/token-webhook-config",
|
||||||
|
Version: "v1beta1",
|
||||||
|
RetryBackoff: apiserveroptions.DefaultAuthWebhookRetryBackoff(),
|
||||||
|
},
|
||||||
|
BootstrapToken: &kubeoptions.BootstrapTokenAuthenticationOptions{},
|
||||||
|
OIDC: &kubeoptions.OIDCAuthenticationOptions{
|
||||||
|
UsernameClaim: "sub",
|
||||||
|
SigningAlgs: []string{"RS256"},
|
||||||
|
},
|
||||||
|
RequestHeader: &apiserveroptions.RequestHeaderAuthenticationOptions{},
|
||||||
|
ServiceAccounts: &kubeoptions.ServiceAccountAuthenticationOptions{
|
||||||
|
Lookup: true,
|
||||||
|
ExtendExpiration: true,
|
||||||
|
},
|
||||||
|
TokenFile: &kubeoptions.TokenFileAuthenticationOptions{},
|
||||||
|
TokenSuccessCacheTTL: 10 * time.Second,
|
||||||
|
TokenFailureCacheTTL: 0,
|
||||||
|
},
|
||||||
|
Authorization: &kubeoptions.BuiltInAuthorizationOptions{
|
||||||
|
Modes: []string{"AlwaysDeny", "RBAC"},
|
||||||
|
PolicyFile: "/policy",
|
||||||
|
WebhookConfigFile: "/webhook-config",
|
||||||
|
WebhookCacheAuthorizedTTL: 180000000000,
|
||||||
|
WebhookCacheUnauthorizedTTL: 60000000000,
|
||||||
|
WebhookVersion: "v1beta1",
|
||||||
|
WebhookRetryBackoff: apiserveroptions.DefaultAuthWebhookRetryBackoff(),
|
||||||
|
},
|
||||||
|
APIEnablement: &apiserveroptions.APIEnablementOptions{
|
||||||
|
RuntimeConfig: cliflag.ConfigurationMap{},
|
||||||
|
},
|
||||||
|
EgressSelector: &apiserveroptions.EgressSelectorOptions{
|
||||||
|
ConfigFile: "/var/run/kubernetes/egress-selector/connectivity.yaml",
|
||||||
|
},
|
||||||
|
EnableLogsHandler: false,
|
||||||
|
EnableAggregatorRouting: true,
|
||||||
|
ProxyClientKeyFile: "/var/run/kubernetes/proxy.key",
|
||||||
|
ProxyClientCertFile: "/var/run/kubernetes/proxy.crt",
|
||||||
|
Metrics: &metrics.Options{},
|
||||||
|
Logs: logs.NewOptions(),
|
||||||
|
Traces: &apiserveroptions.TracingOptions{
|
||||||
|
ConfigFile: "/var/run/kubernetes/tracing_config.yaml",
|
||||||
|
},
|
||||||
|
AggregatorRejectForwardingRedirects: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(expected, s) {
|
||||||
|
t.Errorf("Got different run options than expected.\nDifference detected on:\n%s", cmp.Diff(expected, s, cmpopts.IgnoreUnexported(admission.Plugins{})))
|
||||||
|
}
|
||||||
|
}
|
90
pkg/controlplane/apiserver/options/validation.go
Normal file
90
pkg/controlplane/apiserver/options/validation.go
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 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 (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
|
||||||
|
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||||
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||||
|
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
|
||||||
|
|
||||||
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateTokenRequest(options *Options) []error {
|
||||||
|
var errs []error
|
||||||
|
|
||||||
|
enableAttempted := options.ServiceAccountSigningKeyFile != "" ||
|
||||||
|
(len(options.Authentication.ServiceAccounts.Issuers) != 0 && options.Authentication.ServiceAccounts.Issuers[0] != "") ||
|
||||||
|
len(options.Authentication.APIAudiences) != 0
|
||||||
|
|
||||||
|
enableSucceeded := options.ServiceAccountIssuer != nil
|
||||||
|
|
||||||
|
if !enableAttempted {
|
||||||
|
errs = append(errs, errors.New("--service-account-signing-key-file and --service-account-issuer are required flags"))
|
||||||
|
}
|
||||||
|
|
||||||
|
if enableAttempted && !enableSucceeded {
|
||||||
|
errs = append(errs, errors.New("--service-account-signing-key-file, --service-account-issuer, and --api-audiences should be specified together"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAPIPriorityAndFairness(options *Options) []error {
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.APIPriorityAndFairness) && options.GenericServerRunOptions.EnablePriorityAndFairness {
|
||||||
|
// If none of the following runtime config options are specified,
|
||||||
|
// APF is assumed to be turned on. The internal APF controller uses
|
||||||
|
// v1beta3 so it should be enabled.
|
||||||
|
enabledAPIString := options.APIEnablement.RuntimeConfig.String()
|
||||||
|
testConfigs := []string{"flowcontrol.apiserver.k8s.io/v1beta3", "api/beta", "api/all"} // in the order of precedence
|
||||||
|
for _, testConfig := range testConfigs {
|
||||||
|
if strings.Contains(enabledAPIString, fmt.Sprintf("%s=false", testConfig)) {
|
||||||
|
return []error{fmt.Errorf("--runtime-config=%s=false conflicts with --enable-priority-and-fairness=true and --feature-gates=APIPriorityAndFairness=true", testConfig)}
|
||||||
|
}
|
||||||
|
if strings.Contains(enabledAPIString, fmt.Sprintf("%s=true", testConfig)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate checks Options and return a slice of found errs.
|
||||||
|
func (s *Options) Validate() []error {
|
||||||
|
var errs []error
|
||||||
|
if s.MasterCount <= 0 {
|
||||||
|
errs = append(errs, fmt.Errorf("--apiserver-count should be a positive number, but value '%d' provided", s.MasterCount))
|
||||||
|
}
|
||||||
|
errs = append(errs, s.Etcd.Validate()...)
|
||||||
|
errs = append(errs, validateAPIPriorityAndFairness(s)...)
|
||||||
|
errs = append(errs, s.SecureServing.Validate()...)
|
||||||
|
errs = append(errs, s.Authentication.Validate()...)
|
||||||
|
errs = append(errs, s.Authorization.Validate()...)
|
||||||
|
errs = append(errs, s.Audit.Validate()...)
|
||||||
|
errs = append(errs, s.Admission.Validate()...)
|
||||||
|
errs = append(errs, s.APIEnablement.Validate(legacyscheme.Scheme, apiextensionsapiserver.Scheme, aggregatorscheme.Scheme)...)
|
||||||
|
errs = append(errs, validateTokenRequest(s)...)
|
||||||
|
errs = append(errs, s.Metrics.Validate()...)
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
154
pkg/controlplane/apiserver/options/validation_test.go
Normal file
154
pkg/controlplane/apiserver/options/validation_test.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2023 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 (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
kubeapiserveradmission "k8s.io/apiserver/pkg/admission"
|
||||||
|
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||||
|
basemetrics "k8s.io/component-base/metrics"
|
||||||
|
|
||||||
|
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestValidateAPIPriorityAndFairness(t *testing.T) {
|
||||||
|
const conflict = "conflicts with --enable-priority-and-fairness=true and --feature-gates=APIPriorityAndFairness=true"
|
||||||
|
tests := []struct {
|
||||||
|
runtimeConfig string
|
||||||
|
errShouldContain string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
runtimeConfig: "api/all=false",
|
||||||
|
errShouldContain: conflict,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
runtimeConfig: "api/beta=false",
|
||||||
|
errShouldContain: conflict,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta1=false",
|
||||||
|
errShouldContain: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta2=false",
|
||||||
|
errShouldContain: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta3=false",
|
||||||
|
errShouldContain: conflict,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
runtimeConfig: "flowcontrol.apiserver.k8s.io/v1beta3=true",
|
||||||
|
errShouldContain: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.runtimeConfig, func(t *testing.T) {
|
||||||
|
options := &Options{
|
||||||
|
GenericServerRunOptions: &genericoptions.ServerRunOptions{
|
||||||
|
EnablePriorityAndFairness: true,
|
||||||
|
},
|
||||||
|
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
||||||
|
}
|
||||||
|
options.APIEnablement.RuntimeConfig.Set(test.runtimeConfig)
|
||||||
|
|
||||||
|
var errMessageGot string
|
||||||
|
if errs := validateAPIPriorityAndFairness(options); len(errs) > 0 {
|
||||||
|
errMessageGot = errs[0].Error()
|
||||||
|
}
|
||||||
|
if !strings.Contains(errMessageGot, test.errShouldContain) {
|
||||||
|
t.Errorf("Expected error message to contain: %q, but got: %q", test.errShouldContain, errMessageGot)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateOptions(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
options *Options
|
||||||
|
expectErrors bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "validate master count equal 0",
|
||||||
|
expectErrors: true,
|
||||||
|
options: &Options{
|
||||||
|
MasterCount: 0,
|
||||||
|
GenericServerRunOptions: &genericoptions.ServerRunOptions{},
|
||||||
|
Etcd: &genericoptions.EtcdOptions{},
|
||||||
|
SecureServing: &genericoptions.SecureServingOptionsWithLoopback{},
|
||||||
|
Audit: &genericoptions.AuditOptions{},
|
||||||
|
Admission: &kubeoptions.AdmissionOptions{
|
||||||
|
GenericAdmission: &genericoptions.AdmissionOptions{
|
||||||
|
EnablePlugins: []string{"foo"},
|
||||||
|
Plugins: kubeapiserveradmission.NewPlugins(),
|
||||||
|
},
|
||||||
|
PluginNames: []string{"foo"},
|
||||||
|
},
|
||||||
|
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
|
||||||
|
APIAudiences: []string{"bar"},
|
||||||
|
ServiceAccounts: &kubeoptions.ServiceAccountAuthenticationOptions{
|
||||||
|
Issuers: []string{"baz"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
||||||
|
Metrics: &basemetrics.Options{},
|
||||||
|
ServiceAccountSigningKeyFile: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "validate token request enable not attempted",
|
||||||
|
expectErrors: true,
|
||||||
|
options: &Options{
|
||||||
|
MasterCount: 1,
|
||||||
|
GenericServerRunOptions: &genericoptions.ServerRunOptions{},
|
||||||
|
Etcd: &genericoptions.EtcdOptions{},
|
||||||
|
SecureServing: &genericoptions.SecureServingOptionsWithLoopback{},
|
||||||
|
Audit: &genericoptions.AuditOptions{},
|
||||||
|
Admission: &kubeoptions.AdmissionOptions{
|
||||||
|
GenericAdmission: &genericoptions.AdmissionOptions{
|
||||||
|
EnablePlugins: []string{""},
|
||||||
|
Plugins: kubeapiserveradmission.NewPlugins(),
|
||||||
|
},
|
||||||
|
PluginNames: []string{""},
|
||||||
|
},
|
||||||
|
Authentication: &kubeoptions.BuiltInAuthenticationOptions{
|
||||||
|
ServiceAccounts: &kubeoptions.ServiceAccountAuthenticationOptions{},
|
||||||
|
},
|
||||||
|
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
||||||
|
Metrics: &basemetrics.Options{},
|
||||||
|
ServiceAccountSigningKeyFile: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
errs := tc.options.Validate()
|
||||||
|
if len(errs) > 0 && !tc.expectErrors {
|
||||||
|
t.Errorf("expected no errors, errors found %+v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(errs) == 0 && tc.expectErrors {
|
||||||
|
t.Errorf("expected errors, no errors found")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2016 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package controlplane
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"k8s.io/klog/v2"
|
|
||||||
"k8s.io/utils/integer"
|
|
||||||
utilnet "k8s.io/utils/net"
|
|
||||||
|
|
||||||
kubeoptions "k8s.io/kubernetes/pkg/kubeapiserver/options"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ServiceIPRange checks if the serviceClusterIPRange flag is nil, raising a warning if so and
|
|
||||||
// setting service ip range to the default value in kubeoptions.DefaultServiceIPCIDR
|
|
||||||
// for now until the default is removed per the deprecation timeline guidelines.
|
|
||||||
// Returns service ip range, api server service IP, and an error
|
|
||||||
func ServiceIPRange(passedServiceClusterIPRange net.IPNet) (net.IPNet, net.IP, error) {
|
|
||||||
serviceClusterIPRange := passedServiceClusterIPRange
|
|
||||||
if passedServiceClusterIPRange.IP == nil {
|
|
||||||
klog.Warningf("No CIDR for service cluster IPs specified. Default value which was %s is deprecated and will be removed in future releases. Please specify it using --service-cluster-ip-range on kube-apiserver.", kubeoptions.DefaultServiceIPCIDR.String())
|
|
||||||
serviceClusterIPRange = kubeoptions.DefaultServiceIPCIDR
|
|
||||||
}
|
|
||||||
|
|
||||||
size := integer.Int64Min(utilnet.RangeSize(&serviceClusterIPRange), 1<<16)
|
|
||||||
if size < 8 {
|
|
||||||
return net.IPNet{}, net.IP{}, fmt.Errorf("the service cluster IP range must be at least %d IP addresses", 8)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select the first valid IP from ServiceClusterIPRange to use as the GenericAPIServer service IP.
|
|
||||||
apiServerServiceIP, err := utilnet.GetIndexedIP(&serviceClusterIPRange, 1)
|
|
||||||
if err != nil {
|
|
||||||
return net.IPNet{}, net.IP{}, err
|
|
||||||
}
|
|
||||||
klog.V(4).Infof("Setting service IP to %q (read-write).", apiServerServiceIP)
|
|
||||||
|
|
||||||
return serviceClusterIPRange, apiServerServiceIP, nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user