mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-14 02:43:36 +00:00
vendor updates
This commit is contained in:
51
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/BUILD
generated
vendored
51
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/BUILD
generated
vendored
@ -9,11 +9,12 @@ load(
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"api_enablement.go",
|
||||
"admission.go",
|
||||
"authentication.go",
|
||||
"authorization.go",
|
||||
"cloudprovider.go",
|
||||
"options.go",
|
||||
"plugins.go",
|
||||
"serving.go",
|
||||
"storage_versions.go",
|
||||
],
|
||||
@ -21,20 +22,54 @@ go_library(
|
||||
deps = [
|
||||
"//pkg/api/legacyscheme:go_default_library",
|
||||
"//pkg/client/informers/informers_generated/internalversion:go_default_library",
|
||||
"//pkg/cloudprovider:go_default_library",
|
||||
"//pkg/cloudprovider/providers:go_default_library",
|
||||
"//pkg/kubeapiserver/authenticator:go_default_library",
|
||||
"//pkg/kubeapiserver/authorizer:go_default_library",
|
||||
"//pkg/kubeapiserver/authorizer/modes:go_default_library",
|
||||
"//pkg/kubeapiserver/server:go_default_library",
|
||||
"//plugin/pkg/admission/admit:go_default_library",
|
||||
"//plugin/pkg/admission/alwayspullimages:go_default_library",
|
||||
"//plugin/pkg/admission/antiaffinity:go_default_library",
|
||||
"//plugin/pkg/admission/defaulttolerationseconds:go_default_library",
|
||||
"//plugin/pkg/admission/deny:go_default_library",
|
||||
"//plugin/pkg/admission/eventratelimit:go_default_library",
|
||||
"//plugin/pkg/admission/exec:go_default_library",
|
||||
"//plugin/pkg/admission/extendedresourcetoleration:go_default_library",
|
||||
"//plugin/pkg/admission/gc:go_default_library",
|
||||
"//plugin/pkg/admission/imagepolicy:go_default_library",
|
||||
"//plugin/pkg/admission/initialresources:go_default_library",
|
||||
"//plugin/pkg/admission/limitranger:go_default_library",
|
||||
"//plugin/pkg/admission/namespace/autoprovision:go_default_library",
|
||||
"//plugin/pkg/admission/namespace/exists:go_default_library",
|
||||
"//plugin/pkg/admission/noderestriction:go_default_library",
|
||||
"//plugin/pkg/admission/persistentvolume/label:go_default_library",
|
||||
"//plugin/pkg/admission/persistentvolume/resize:go_default_library",
|
||||
"//plugin/pkg/admission/podnodeselector:go_default_library",
|
||||
"//plugin/pkg/admission/podpreset:go_default_library",
|
||||
"//plugin/pkg/admission/podtolerationrestriction:go_default_library",
|
||||
"//plugin/pkg/admission/priority:go_default_library",
|
||||
"//plugin/pkg/admission/resourcequota:go_default_library",
|
||||
"//plugin/pkg/admission/security/podsecuritypolicy:go_default_library",
|
||||
"//plugin/pkg/admission/securitycontext/scdeny:go_default_library",
|
||||
"//plugin/pkg/admission/serviceaccount:go_default_library",
|
||||
"//plugin/pkg/admission/storage/storageclass/setdefault:go_default_library",
|
||||
"//plugin/pkg/admission/storage/storageobjectinuseprotection:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/github.com/pborman/uuid:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/plugin/initialization:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission/plugin/webhook/validating:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/options:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/util/flag:go_default_library",
|
||||
"//vendor/k8s.io/client-go/informers:go_default_library",
|
||||
"//vendor/k8s.io/client-go/rest:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
@ -53,8 +88,10 @@ filegroup(
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["storage_versions_test.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/kubeapiserver/options",
|
||||
library = ":go_default_library",
|
||||
srcs = [
|
||||
"admission_test.go",
|
||||
"storage_versions_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library"],
|
||||
)
|
||||
|
128
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/admission.go
generated
vendored
Normal file
128
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/admission.go
generated
vendored
Normal file
@ -0,0 +1,128 @@
|
||||
/*
|
||||
Copyright 2018 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/rest"
|
||||
)
|
||||
|
||||
// AdmissionOptions holds the admission options.
|
||||
// It is a wrap of generic AdmissionOptions.
|
||||
type AdmissionOptions struct {
|
||||
// GenericAdmission holds the generic admission options.
|
||||
GenericAdmission *genericoptions.AdmissionOptions
|
||||
// DEPRECATED flag, should use EnabledAdmissionPlugins and DisabledAdmissionPlugins.
|
||||
// They are mutually exclusive, specify both will lead to an error.
|
||||
PluginNames []string
|
||||
}
|
||||
|
||||
// NewAdmissionOptions creates a new instance of AdmissionOptions
|
||||
// Note:
|
||||
// In addition it calls RegisterAllAdmissionPlugins to register
|
||||
// all kube-apiserver admission plugins.
|
||||
//
|
||||
// Provides the list of RecommendedPluginOrder that holds sane values
|
||||
// that can be used by servers that don't care about admission chain.
|
||||
// Servers that do care can overwrite/append that field after creation.
|
||||
func NewAdmissionOptions() *AdmissionOptions {
|
||||
options := genericoptions.NewAdmissionOptions()
|
||||
// register all admission plugins
|
||||
RegisterAllAdmissionPlugins(options.Plugins)
|
||||
// set RecommendedPluginOrder
|
||||
options.RecommendedPluginOrder = AllOrderedPlugins
|
||||
// set DefaultOffPlugins
|
||||
options.DefaultOffPlugins = DefaultOffAdmissionPlugins()
|
||||
|
||||
return &AdmissionOptions{
|
||||
GenericAdmission: options,
|
||||
}
|
||||
}
|
||||
|
||||
// AddFlags adds flags related to admission for kube-apiserver to the specified FlagSet
|
||||
func (a *AdmissionOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringSliceVar(&a.PluginNames, "admission-control", a.PluginNames, ""+
|
||||
"Admission is divided into two phases. "+
|
||||
"In the first phase, only mutating admission plugins run. "+
|
||||
"In the second phase, only validating admission plugins run. "+
|
||||
"The names in the below list may represent a validating plugin, a mutating plugin, or both. "+
|
||||
"The order of plugins in which they are passed to this flag does not matter. "+
|
||||
"Comma-delimited list of: "+strings.Join(a.GenericAdmission.Plugins.Registered(), ", ")+".")
|
||||
fs.MarkDeprecated("admission-control", "Use --enable-admission-plugins or --disable-admission-plugins instead. Will be removed in a future version.")
|
||||
|
||||
a.GenericAdmission.AddFlags(fs)
|
||||
}
|
||||
|
||||
// Validate verifies flags passed to kube-apiserver AdmissionOptions.
|
||||
// Kube-apiserver verifies PluginNames and then call generic AdmissionOptions.Validate.
|
||||
func (a *AdmissionOptions) Validate() []error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
errs := []error{}
|
||||
if a.PluginNames != nil &&
|
||||
(a.GenericAdmission.EnablePlugins != nil || a.GenericAdmission.DisablePlugins != nil) {
|
||||
errs = append(errs, fmt.Errorf("admission-control and enable-admission-plugins/disable-admission-plugins flags are mutually exclusive"))
|
||||
}
|
||||
|
||||
registeredPlugins := sets.NewString(a.GenericAdmission.Plugins.Registered()...)
|
||||
for _, name := range a.PluginNames {
|
||||
if !registeredPlugins.Has(name) {
|
||||
errs = append(errs, fmt.Errorf("admission-control plugin %q is unknown", name))
|
||||
}
|
||||
}
|
||||
|
||||
errs = append(errs, a.GenericAdmission.Validate()...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// ApplyTo adds the admission chain to the server configuration.
|
||||
// Kube-apiserver just call generic AdmissionOptions.ApplyTo.
|
||||
func (a *AdmissionOptions) ApplyTo(
|
||||
c *server.Config,
|
||||
informers informers.SharedInformerFactory,
|
||||
kubeAPIServerClientConfig *rest.Config,
|
||||
scheme *runtime.Scheme,
|
||||
pluginInitializers ...admission.PluginInitializer,
|
||||
) error {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if a.PluginNames != nil {
|
||||
// pass PluginNames to generic AdmissionOptions
|
||||
a.GenericAdmission.EnablePlugins, a.GenericAdmission.DisablePlugins = computePluginNames(a.PluginNames, a.GenericAdmission.RecommendedPluginOrder)
|
||||
}
|
||||
|
||||
return a.GenericAdmission.ApplyTo(c, informers, kubeAPIServerClientConfig, scheme, pluginInitializers...)
|
||||
}
|
||||
|
||||
// explicitly disable all plugins that are not in the enabled list
|
||||
func computePluginNames(explicitlyEnabled []string, all []string) (enabled []string, disabled []string) {
|
||||
return explicitlyEnabled, sets.NewString(all...).Difference(sets.NewString(explicitlyEnabled...)).List()
|
||||
}
|
88
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/admission_test.go
generated
vendored
Normal file
88
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/admission_test.go
generated
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
Copyright 2018 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"
|
||||
)
|
||||
|
||||
func TestValidate(t *testing.T) {
|
||||
// 1. Both `--admission-control` and `--enable-admission-plugins` are specified
|
||||
options := NewAdmissionOptions()
|
||||
options.PluginNames = []string{"ServiceAccount"}
|
||||
options.GenericAdmission.EnablePlugins = []string{"Initializers"}
|
||||
if len(options.Validate()) == 0 {
|
||||
t.Errorf("Expect error, but got none")
|
||||
}
|
||||
|
||||
// 2. Both `--admission-control` and `--disable-admission-plugins` are specified
|
||||
options = NewAdmissionOptions()
|
||||
options.PluginNames = []string{"ServiceAccount"}
|
||||
options.GenericAdmission.DisablePlugins = []string{"Initializers"}
|
||||
if len(options.Validate()) == 0 {
|
||||
t.Errorf("Expect error, but got none")
|
||||
}
|
||||
|
||||
// 3. PluginNames is not registered
|
||||
options = NewAdmissionOptions()
|
||||
options.PluginNames = []string{"pluginA"}
|
||||
if len(options.Validate()) == 0 {
|
||||
t.Errorf("Expect error, but got none")
|
||||
}
|
||||
|
||||
// 4. PluginNames is not valid
|
||||
options = NewAdmissionOptions()
|
||||
options.PluginNames = []string{"ServiceAccount"}
|
||||
if errs := options.Validate(); len(errs) > 0 {
|
||||
t.Errorf("Unexpected err: %v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestComputeEnabledAdmission(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
all []string
|
||||
enabled []string
|
||||
expectedDisabled []string
|
||||
}{
|
||||
{
|
||||
name: "matches",
|
||||
all: []string{"one", "two"},
|
||||
enabled: []string{"one", "two"},
|
||||
expectedDisabled: []string{},
|
||||
},
|
||||
{
|
||||
name: "choose one",
|
||||
all: []string{"one", "two"},
|
||||
enabled: []string{"one"},
|
||||
expectedDisabled: []string{"two"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
actualEnabled, actualDisabled := computePluginNames(tc.enabled, tc.all)
|
||||
if e, a := tc.enabled, actualEnabled; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := tc.expectedDisabled, actualDisabled; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
44
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/api_enablement.go
generated
vendored
44
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/api_enablement.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
utilflag "k8s.io/apiserver/pkg/util/flag"
|
||||
)
|
||||
|
||||
// APIEnablementOptions contains the options for which resources to turn on and off.
|
||||
// Given small aggregated API servers, this option isn't required for "normal" API servers
|
||||
type APIEnablementOptions struct {
|
||||
RuntimeConfig utilflag.ConfigurationMap
|
||||
}
|
||||
|
||||
func NewAPIEnablementOptions() *APIEnablementOptions {
|
||||
return &APIEnablementOptions{
|
||||
RuntimeConfig: make(utilflag.ConfigurationMap),
|
||||
}
|
||||
}
|
||||
|
||||
// AddFlags adds flags for a specific APIServer to the specified FlagSet
|
||||
func (s *APIEnablementOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.Var(&s.RuntimeConfig, "runtime-config", ""+
|
||||
"A set of key=value pairs that describe runtime configuration that may be passed "+
|
||||
"to apiserver. apis/<groupVersion> key can be used to turn on/off specific api versions. "+
|
||||
"apis/<groupVersion>/<resource> can be used to turn on/off specific resources. api/all and "+
|
||||
"api/legacy are special keys to control all and legacy api versions respectively.")
|
||||
}
|
69
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/authentication.go
generated
vendored
69
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/authentication.go
generated
vendored
@ -18,6 +18,7 @@ package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -34,7 +35,6 @@ type BuiltInAuthenticationOptions struct {
|
||||
Anonymous *AnonymousAuthenticationOptions
|
||||
BootstrapToken *BootstrapTokenAuthenticationOptions
|
||||
ClientCert *genericoptions.ClientCertAuthenticationOptions
|
||||
Keystone *KeystoneAuthenticationOptions
|
||||
OIDC *OIDCAuthenticationOptions
|
||||
PasswordFile *PasswordFileAuthenticationOptions
|
||||
RequestHeader *genericoptions.RequestHeaderAuthenticationOptions
|
||||
@ -54,11 +54,6 @@ type BootstrapTokenAuthenticationOptions struct {
|
||||
Enable bool
|
||||
}
|
||||
|
||||
type KeystoneAuthenticationOptions struct {
|
||||
URL string
|
||||
CAFile string
|
||||
}
|
||||
|
||||
type OIDCAuthenticationOptions struct {
|
||||
CAFile string
|
||||
ClientID string
|
||||
@ -67,6 +62,7 @@ type OIDCAuthenticationOptions struct {
|
||||
UsernamePrefix string
|
||||
GroupsClaim string
|
||||
GroupsPrefix string
|
||||
SigningAlgs []string
|
||||
}
|
||||
|
||||
type PasswordFileAuthenticationOptions struct {
|
||||
@ -74,8 +70,10 @@ type PasswordFileAuthenticationOptions struct {
|
||||
}
|
||||
|
||||
type ServiceAccountAuthenticationOptions struct {
|
||||
KeyFiles []string
|
||||
Lookup bool
|
||||
KeyFiles []string
|
||||
Lookup bool
|
||||
Issuer string
|
||||
APIAudiences []string
|
||||
}
|
||||
|
||||
type TokenFileAuthenticationOptions struct {
|
||||
@ -99,7 +97,6 @@ func (s *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions {
|
||||
WithAnonymous().
|
||||
WithBootstrapToken().
|
||||
WithClientCert().
|
||||
WithKeystone().
|
||||
WithOIDC().
|
||||
WithPasswordFile().
|
||||
WithRequestHeader().
|
||||
@ -123,11 +120,6 @@ func (s *BuiltInAuthenticationOptions) WithClientCert() *BuiltInAuthenticationOp
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *BuiltInAuthenticationOptions) WithKeystone() *BuiltInAuthenticationOptions {
|
||||
s.Keystone = &KeystoneAuthenticationOptions{}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *BuiltInAuthenticationOptions) WithOIDC() *BuiltInAuthenticationOptions {
|
||||
s.OIDC = &OIDCAuthenticationOptions{}
|
||||
return s
|
||||
@ -168,6 +160,12 @@ func (s *BuiltInAuthenticationOptions) Validate() []error {
|
||||
allErrors = append(allErrors, fmt.Errorf("oidc-issuer-url and oidc-client-id should be specified together"))
|
||||
}
|
||||
|
||||
if s.ServiceAccounts != nil && len(s.ServiceAccounts.Issuer) > 0 && strings.Contains(s.ServiceAccounts.Issuer, ":") {
|
||||
if _, err := url.Parse(s.ServiceAccounts.Issuer); err != nil {
|
||||
allErrors = append(allErrors, fmt.Errorf("service-account-issuer contained a ':' but was not a valid URL: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
@ -189,15 +187,6 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
s.ClientCert.AddFlags(fs)
|
||||
}
|
||||
|
||||
if s.Keystone != nil {
|
||||
fs.StringVar(&s.Keystone.URL, "experimental-keystone-url", s.Keystone.URL,
|
||||
"If passed, activates the keystone authentication plugin.")
|
||||
|
||||
fs.StringVar(&s.Keystone.CAFile, "experimental-keystone-ca-file", s.Keystone.CAFile, ""+
|
||||
"If set, the Keystone server's certificate will be verified by one of the authorities "+
|
||||
"in the experimental-keystone-ca-file, otherwise the host's root CA set will be used.")
|
||||
}
|
||||
|
||||
if s.OIDC != nil {
|
||||
fs.StringVar(&s.OIDC.IssuerURL, "oidc-issuer-url", s.OIDC.IssuerURL, ""+
|
||||
"The URL of the OpenID issuer, only HTTPS scheme will be accepted. "+
|
||||
@ -229,6 +218,10 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
"If provided, all groups will be prefixed with this value to prevent conflicts with "+
|
||||
"other authentication strategies.")
|
||||
|
||||
fs.StringSliceVar(&s.OIDC.SigningAlgs, "oidc-signing-algs", []string{"RS256"}, ""+
|
||||
"Comma-separated list of allowed JOSE asymmetric signing algorithms. JWTs with a "+
|
||||
"'alg' header value not in this list will be rejected. "+
|
||||
"Values are defined by RFC 7518 https://tools.ietf.org/html/rfc7518#section-3.1.")
|
||||
}
|
||||
|
||||
if s.PasswordFile != nil {
|
||||
@ -244,11 +237,21 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if s.ServiceAccounts != nil {
|
||||
fs.StringArrayVar(&s.ServiceAccounts.KeyFiles, "service-account-key-file", s.ServiceAccounts.KeyFiles, ""+
|
||||
"File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to verify "+
|
||||
"ServiceAccount tokens. If unspecified, --tls-private-key-file is used. "+
|
||||
"The specified file can contain multiple keys, and the flag can be specified multiple times with different files.")
|
||||
"ServiceAccount tokens. The specified file can contain multiple keys, and the flag can "+
|
||||
"be specified multiple times with different files. If unspecified, "+
|
||||
"--tls-private-key-file is used. Must be specified when "+
|
||||
"--service-account-signing-key is provided")
|
||||
|
||||
fs.BoolVar(&s.ServiceAccounts.Lookup, "service-account-lookup", s.ServiceAccounts.Lookup,
|
||||
"If true, validate ServiceAccount tokens exist in etcd as part of authentication.")
|
||||
|
||||
fs.StringVar(&s.ServiceAccounts.Issuer, "service-account-issuer", s.ServiceAccounts.Issuer, ""+
|
||||
"Identifier of the service account token issuer. The issuer will assert this identifier "+
|
||||
"in \"iss\" claim of issued tokens. This value is a string or URI.")
|
||||
|
||||
fs.StringSliceVar(&s.ServiceAccounts.APIAudiences, "service-account-api-audiences", s.ServiceAccounts.APIAudiences, ""+
|
||||
"Identifiers of the API. The service account token authenticator will validate that "+
|
||||
"tokens used against the API are bound to at least one of these audiences.")
|
||||
}
|
||||
|
||||
if s.TokenFile != nil {
|
||||
@ -285,11 +288,6 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.Au
|
||||
ret.ClientCAFile = s.ClientCert.ClientCA
|
||||
}
|
||||
|
||||
if s.Keystone != nil {
|
||||
ret.KeystoneURL = s.Keystone.URL
|
||||
ret.KeystoneCAFile = s.Keystone.CAFile
|
||||
}
|
||||
|
||||
if s.OIDC != nil {
|
||||
ret.OIDCCAFile = s.OIDC.CAFile
|
||||
ret.OIDCClientID = s.OIDC.ClientID
|
||||
@ -298,6 +296,7 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.Au
|
||||
ret.OIDCIssuerURL = s.OIDC.IssuerURL
|
||||
ret.OIDCUsernameClaim = s.OIDC.UsernameClaim
|
||||
ret.OIDCUsernamePrefix = s.OIDC.UsernamePrefix
|
||||
ret.OIDCSigningAlgs = s.OIDC.SigningAlgs
|
||||
}
|
||||
|
||||
if s.PasswordFile != nil {
|
||||
@ -311,6 +310,8 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() authenticator.Au
|
||||
if s.ServiceAccounts != nil {
|
||||
ret.ServiceAccountKeyFiles = s.ServiceAccounts.KeyFiles
|
||||
ret.ServiceAccountLookup = s.ServiceAccounts.Lookup
|
||||
ret.ServiceAccountIssuer = s.ServiceAccounts.Issuer
|
||||
ret.ServiceAccountAPIAudiences = s.ServiceAccounts.APIAudiences
|
||||
}
|
||||
|
||||
if s.TokenFile != nil {
|
||||
@ -341,19 +342,17 @@ func (o *BuiltInAuthenticationOptions) ApplyTo(c *genericapiserver.Config) error
|
||||
|
||||
var err error
|
||||
if o.ClientCert != nil {
|
||||
c, err = c.ApplyClientCert(o.ClientCert.ClientCA)
|
||||
if err != nil {
|
||||
if err = c.Authentication.ApplyClientCert(o.ClientCert.ClientCA, c.SecureServing); err != nil {
|
||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
}
|
||||
if o.RequestHeader != nil {
|
||||
c, err = c.ApplyClientCert(o.RequestHeader.ClientCAFile)
|
||||
if err != nil {
|
||||
if err = c.Authentication.ApplyClientCert(o.RequestHeader.ClientCAFile, c.SecureServing); err != nil {
|
||||
return fmt.Errorf("unable to load client CA file: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.SupportsBasicAuth = o.PasswordFile != nil && len(o.PasswordFile.BasicAuthFile) > 0
|
||||
c.Authentication.SupportsBasicAuth = o.PasswordFile != nil && len(o.PasswordFile.BasicAuthFile) > 0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
4
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/authorization.go
generated
vendored
4
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/authorization.go
generated
vendored
@ -22,6 +22,7 @@ import (
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
versionedinformers "k8s.io/client-go/informers"
|
||||
informers "k8s.io/kubernetes/pkg/client/informers/informers_generated/internalversion"
|
||||
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer"
|
||||
authzmodes "k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
|
||||
@ -83,7 +84,7 @@ func (s *BuiltInAuthorizationOptions) Modes() []string {
|
||||
return modes
|
||||
}
|
||||
|
||||
func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory informers.SharedInformerFactory) authorizer.AuthorizationConfig {
|
||||
func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory informers.SharedInformerFactory, versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.AuthorizationConfig {
|
||||
return authorizer.AuthorizationConfig{
|
||||
AuthorizationModes: s.Modes(),
|
||||
PolicyFile: s.PolicyFile,
|
||||
@ -91,5 +92,6 @@ func (s *BuiltInAuthorizationOptions) ToAuthorizationConfig(informerFactory info
|
||||
WebhookCacheAuthorizedTTL: s.WebhookCacheAuthorizedTTL,
|
||||
WebhookCacheUnauthorizedTTL: s.WebhookCacheUnauthorizedTTL,
|
||||
InformerFactory: informerFactory,
|
||||
VersionedInformerFactory: versionedInformerFactory,
|
||||
}
|
||||
}
|
||||
|
49
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/cloudprovider.go
generated
vendored
49
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/cloudprovider.go
generated
vendored
@ -17,15 +17,7 @@ limitations under the License.
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||
"k8s.io/kubernetes/pkg/cloudprovider"
|
||||
)
|
||||
|
||||
type CloudProviderOptions struct {
|
||||
@ -49,44 +41,3 @@ func (s *CloudProviderOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.StringVar(&s.CloudConfigFile, "cloud-config", s.CloudConfigFile,
|
||||
"The path to the cloud provider configuration file. Empty string for no configuration file.")
|
||||
}
|
||||
|
||||
func (s *CloudProviderOptions) DefaultExternalHost(genericoptions *genericoptions.ServerRunOptions) error {
|
||||
if len(genericoptions.ExternalHost) != 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if cloudprovider.IsCloudProvider(s.CloudProvider) {
|
||||
glog.Info("--external-hostname was not specified. Trying to get it from the cloud provider.")
|
||||
|
||||
cloud, err := cloudprovider.InitCloudProvider(s.CloudProvider, s.CloudConfigFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q cloud provider could not be initialized: %v", s.CloudProvider, err)
|
||||
}
|
||||
instances, supported := cloud.Instances()
|
||||
if !supported {
|
||||
return fmt.Errorf("%q cloud provider has no instances", s.CloudProvider)
|
||||
}
|
||||
hostname, err := os.Hostname()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get hostname: %v", err)
|
||||
}
|
||||
nodeName, err := instances.CurrentNodeName(hostname)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get NodeName from %q cloud provider: %v", s.CloudProvider, err)
|
||||
}
|
||||
addrs, err := instances.NodeAddresses(nodeName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get external host address from %q cloud provider: %v", s.CloudProvider, err)
|
||||
} else {
|
||||
for _, addr := range addrs {
|
||||
if addr.Type == v1.NodeExternalIP {
|
||||
genericoptions.ExternalHost = addr.Address
|
||||
glog.Warning("[Deprecated] Getting host address using cloud provider is " +
|
||||
"now deprecated. Please use --external-hostname explicitly")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
5
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/options.go
generated
vendored
5
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/options.go
generated
vendored
@ -17,8 +17,13 @@ limitations under the License.
|
||||
package options
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
)
|
||||
|
||||
// DefaultServiceNodePortRange is the default port range for NodePort services.
|
||||
var DefaultServiceNodePortRange = utilnet.PortRange{Base: 30000, Size: 2768}
|
||||
|
||||
// DefaultServiceIPCIDR is a CIDR notation of IP range from which to allocate service cluster IPs
|
||||
var DefaultServiceIPCIDR net.IPNet = net.IPNet{IP: net.ParseIP("10.0.0.0"), Mask: net.CIDRMask(24, 32)}
|
||||
|
146
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/plugins.go
generated
vendored
Normal file
146
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/plugins.go
generated
vendored
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package options
|
||||
|
||||
// This file exists to force the desired plugin implementations to be linked.
|
||||
// This should probably be part of some configuration fed into the build for a
|
||||
// given binary target.
|
||||
import (
|
||||
// Cloud providers
|
||||
_ "k8s.io/kubernetes/pkg/cloudprovider/providers"
|
||||
|
||||
// Admission policies
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/admit"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/alwayspullimages"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/antiaffinity"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/defaulttolerationseconds"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/deny"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/eventratelimit"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/exec"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/extendedresourcetoleration"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/gc"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/imagepolicy"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/initialresources"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/limitranger"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/namespace/exists"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/noderestriction"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/label"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/persistentvolume/resize"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/podnodeselector"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/podpreset"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/podtolerationrestriction"
|
||||
podpriority "k8s.io/kubernetes/plugin/pkg/admission/priority"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/security/podsecuritypolicy"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/serviceaccount"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/storage/storageclass/setdefault"
|
||||
"k8s.io/kubernetes/plugin/pkg/admission/storage/storageobjectinuseprotection"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/initialization"
|
||||
"k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle"
|
||||
mutatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/mutating"
|
||||
validatingwebhook "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
|
||||
)
|
||||
|
||||
// AllOrderedPlugins is the list of all the plugins in order.
|
||||
var AllOrderedPlugins = []string{
|
||||
admit.PluginName, // AlwaysAdmit
|
||||
autoprovision.PluginName, // NamespaceAutoProvision
|
||||
lifecycle.PluginName, // NamespaceLifecycle
|
||||
exists.PluginName, // NamespaceExists
|
||||
scdeny.PluginName, // SecurityContextDeny
|
||||
antiaffinity.PluginName, // LimitPodHardAntiAffinityTopology
|
||||
initialresources.PluginName, // InitialResources
|
||||
podpreset.PluginName, // PodPreset
|
||||
limitranger.PluginName, // LimitRanger
|
||||
serviceaccount.PluginName, // ServiceAccount
|
||||
noderestriction.PluginName, // NodeRestriction
|
||||
alwayspullimages.PluginName, // AlwaysPullImages
|
||||
imagepolicy.PluginName, // ImagePolicyWebhook
|
||||
podsecuritypolicy.PluginName, // PodSecurityPolicy
|
||||
podnodeselector.PluginName, // PodNodeSelector
|
||||
podpriority.PluginName, // Priority
|
||||
defaulttolerationseconds.PluginName, // DefaultTolerationSeconds
|
||||
podtolerationrestriction.PluginName, // PodTolerationRestriction
|
||||
exec.DenyEscalatingExec, // DenyEscalatingExec
|
||||
exec.DenyExecOnPrivileged, // DenyExecOnPrivileged
|
||||
eventratelimit.PluginName, // EventRateLimit
|
||||
extendedresourcetoleration.PluginName, // ExtendedResourceToleration
|
||||
label.PluginName, // PersistentVolumeLabel
|
||||
setdefault.PluginName, // DefaultStorageClass
|
||||
storageobjectinuseprotection.PluginName, // StorageObjectInUseProtection
|
||||
gc.PluginName, // OwnerReferencesPermissionEnforcement
|
||||
resize.PluginName, // PersistentVolumeClaimResize
|
||||
mutatingwebhook.PluginName, // MutatingAdmissionWebhook
|
||||
initialization.PluginName, // Initializers
|
||||
validatingwebhook.PluginName, // ValidatingAdmissionWebhook
|
||||
resourcequota.PluginName, // ResourceQuota
|
||||
deny.PluginName, // AlwaysDeny
|
||||
}
|
||||
|
||||
// RegisterAllAdmissionPlugins registers all admission plugins and
|
||||
// sets the recommended plugins order.
|
||||
func RegisterAllAdmissionPlugins(plugins *admission.Plugins) {
|
||||
admit.Register(plugins) // DEPRECATED as no real meaning
|
||||
alwayspullimages.Register(plugins)
|
||||
antiaffinity.Register(plugins)
|
||||
defaulttolerationseconds.Register(plugins)
|
||||
deny.Register(plugins) // DEPRECATED as no real meaning
|
||||
eventratelimit.Register(plugins)
|
||||
exec.Register(plugins)
|
||||
extendedresourcetoleration.Register(plugins)
|
||||
gc.Register(plugins)
|
||||
imagepolicy.Register(plugins)
|
||||
initialresources.Register(plugins)
|
||||
limitranger.Register(plugins)
|
||||
autoprovision.Register(plugins)
|
||||
exists.Register(plugins)
|
||||
noderestriction.Register(plugins)
|
||||
label.Register(plugins) // DEPRECATED in favor of NewPersistentVolumeLabelController in CCM
|
||||
podnodeselector.Register(plugins)
|
||||
podpreset.Register(plugins)
|
||||
podtolerationrestriction.Register(plugins)
|
||||
resourcequota.Register(plugins)
|
||||
podsecuritypolicy.Register(plugins)
|
||||
podpriority.Register(plugins)
|
||||
scdeny.Register(plugins)
|
||||
serviceaccount.Register(plugins)
|
||||
setdefault.Register(plugins)
|
||||
resize.Register(plugins)
|
||||
storageobjectinuseprotection.Register(plugins)
|
||||
}
|
||||
|
||||
// DefaultOffAdmissionPlugins get admission plugins off by default for kube-apiserver.
|
||||
func DefaultOffAdmissionPlugins() sets.String {
|
||||
defaultOnPlugins := sets.NewString(
|
||||
lifecycle.PluginName, //NamespaceLifecycle
|
||||
limitranger.PluginName, //LimitRanger
|
||||
serviceaccount.PluginName, //ServiceAccount
|
||||
label.PluginName, //PersistentVolumeLabel
|
||||
setdefault.PluginName, //DefaultStorageClass
|
||||
defaulttolerationseconds.PluginName, //DefaultTolerationSeconds
|
||||
mutatingwebhook.PluginName, //MutatingAdmissionWebhook
|
||||
validatingwebhook.PluginName, //ValidatingAdmissionWebhook
|
||||
resourcequota.PluginName, //ResourceQuota
|
||||
)
|
||||
|
||||
return sets.NewString(AllOrderedPlugins...).Difference(defaultOnPlugins)
|
||||
}
|
23
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/serving.go
generated
vendored
23
vendor/k8s.io/kubernetes/pkg/kubeapiserver/options/serving.go
generated
vendored
@ -14,7 +14,7 @@ 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 contains flags and options for initializing kube-apiserver
|
||||
package options
|
||||
|
||||
import (
|
||||
@ -31,17 +31,17 @@ import (
|
||||
kubeserver "k8s.io/kubernetes/pkg/kubeapiserver/server"
|
||||
)
|
||||
|
||||
// NewSecureServingOptions gives default values for the kube-apiserver and federation-apiserver which are not the options wanted by
|
||||
// NewSecureServingOptions gives default values for the kube-apiserver which are not the options wanted by
|
||||
// "normal" API servers running on the platform
|
||||
func NewSecureServingOptions() *genericoptions.SecureServingOptions {
|
||||
return &genericoptions.SecureServingOptions{
|
||||
func NewSecureServingOptions() *genericoptions.SecureServingOptionsWithLoopback {
|
||||
return genericoptions.WithLoopback(&genericoptions.SecureServingOptions{
|
||||
BindAddress: net.ParseIP("0.0.0.0"),
|
||||
BindPort: 6443,
|
||||
ServerCert: genericoptions.GeneratableKeyCert{
|
||||
PairName: "apiserver",
|
||||
CertDirectory: "/var/run/kubernetes",
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// DefaultAdvertiseAddress sets the field AdvertiseAddress if
|
||||
@ -56,8 +56,8 @@ func DefaultAdvertiseAddress(s *genericoptions.ServerRunOptions, insecure *Insec
|
||||
if s.AdvertiseAddress == nil || s.AdvertiseAddress.IsUnspecified() {
|
||||
hostIP, err := insecure.DefaultExternalAddress()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to find suitable network address.error='%v'. "+
|
||||
"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this.", err)
|
||||
return fmt.Errorf("unable to find suitable network address.error='%v'. "+
|
||||
"Try to set the AdvertiseAddress directly or provide a valid BindAddress to fix this", err)
|
||||
}
|
||||
s.AdvertiseAddress = hostIP
|
||||
}
|
||||
@ -85,7 +85,7 @@ func (s InsecureServingOptions) Validate(portArg string) []error {
|
||||
errors := []error{}
|
||||
|
||||
if s.BindPort < 0 || s.BindPort > 65535 {
|
||||
errors = append(errors, fmt.Errorf("--insecure-port %v must be between 0 and 65535, inclusive. 0 for turning off secure port.", s.BindPort))
|
||||
errors = append(errors, fmt.Errorf("--insecure-port %v must be between 0 and 65535, inclusive. 0 for turning off insecure (HTTP) port", s.BindPort))
|
||||
}
|
||||
|
||||
return errors
|
||||
@ -97,15 +97,18 @@ func (s *InsecureServingOptions) DefaultExternalAddress() (net.IP, error) {
|
||||
|
||||
func (s *InsecureServingOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
fs.IPVar(&s.BindAddress, "insecure-bind-address", s.BindAddress, ""+
|
||||
"The IP address on which to serve the --insecure-port (set to 0.0.0.0 for all interfaces).")
|
||||
"The IP address on which to serve the --insecure-port (set to 0.0.0.0 for all IPv4 interfaces and :: for all IPv6 interfaces).")
|
||||
fs.MarkDeprecated("insecure-bind-address", "This flag will be removed in a future version.")
|
||||
|
||||
fs.IntVar(&s.BindPort, "insecure-port", s.BindPort, ""+
|
||||
"The port on which to serve unsecured, unauthenticated access. It is assumed "+
|
||||
"that firewall rules are set up such that this port is not reachable from outside of "+
|
||||
"the cluster and that port 443 on the cluster's public address is proxied to this "+
|
||||
"port. This is performed by nginx in the default setup.")
|
||||
"port. This is performed by nginx in the default setup. Set to zero to disable.")
|
||||
fs.MarkDeprecated("insecure-port", "This flag will be removed in a future version.")
|
||||
}
|
||||
|
||||
// TODO: remove it until kops stop using `--address`
|
||||
func (s *InsecureServingOptions) AddDeprecatedFlags(fs *pflag.FlagSet) {
|
||||
fs.IPVar(&s.BindAddress, "address", s.BindAddress,
|
||||
"DEPRECATED: see --insecure-bind-address instead.")
|
||||
|
Reference in New Issue
Block a user