mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 02:34:03 +00:00
Add --authorization-config flag to apiserver
Signed-off-by: Nabarun Pal <pal.nabarun95@gmail.com>
This commit is contained in:
parent
007ef653ad
commit
22e5a806a7
@ -327,6 +327,13 @@ func TestAddFlags(t *testing.T) {
|
||||
expected.Authentication.OIDC.UsernameClaim = "sub"
|
||||
expected.Authentication.OIDC.SigningAlgs = []string{"RS256"}
|
||||
|
||||
if !s.Authorization.AreLegacyFlagsSet() {
|
||||
t.Errorf("expected legacy authorization flags to be set")
|
||||
}
|
||||
|
||||
// setting the method to nil since methods can't be compared with reflect.DeepEqual
|
||||
s.Authorization.AreLegacyFlagsSet = nil
|
||||
|
||||
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{}, kubeoptions.OIDCAuthenticationOptions{})))
|
||||
}
|
||||
|
@ -283,6 +283,12 @@ func TestAddFlags(t *testing.T) {
|
||||
expected.Authentication.OIDC.UsernameClaim = "sub"
|
||||
expected.Authentication.OIDC.SigningAlgs = []string{"RS256"}
|
||||
|
||||
if !s.Authorization.AreLegacyFlagsSet() {
|
||||
t.Errorf("expected legacy authorization flags to be set")
|
||||
}
|
||||
// setting the method to nil since methods can't be compared with reflect.DeepEqual
|
||||
s.Authorization.AreLegacyFlagsSet = nil
|
||||
|
||||
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{}, kubeoptions.OIDCAuthenticationOptions{})))
|
||||
}
|
||||
|
@ -21,12 +21,17 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apiserver/pkg/apis/apiserver/load"
|
||||
genericfeatures "k8s.io/apiserver/pkg/features"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver/validation"
|
||||
genericoptions "k8s.io/apiserver/pkg/server/options"
|
||||
versionedinformers "k8s.io/client-go/informers"
|
||||
|
||||
@ -35,9 +40,19 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
defaultWebhookName = "default"
|
||||
defaultWebhookName = "default"
|
||||
authorizationModeFlag = "authorization-mode"
|
||||
authorizationWebhookConfigFileFlag = "authorization-webhook-config-file"
|
||||
authorizationWebhookVersionFlag = "authorization-webhook-version"
|
||||
authorizationWebhookAuthorizedTTLFlag = "authorization-webhook-cache-authorized-ttl"
|
||||
authorizationWebhookUnauthorizedTTLFlag = "authorization-webhook-cache-unauthorized-ttl"
|
||||
authorizationPolicyFileFlag = "authorization-policy-file"
|
||||
authorizationConfigFlag = "authorization-config"
|
||||
)
|
||||
|
||||
// RepeatableAuthorizerTypes is the list of Authorizer that can be repeated in the Authorization Config
|
||||
var repeatableAuthorizerTypes = []string{authzmodes.ModeWebhook}
|
||||
|
||||
// BuiltInAuthorizationOptions contains all build-in authorization options for API Server
|
||||
type BuiltInAuthorizationOptions struct {
|
||||
Modes []string
|
||||
@ -50,6 +65,16 @@ type BuiltInAuthorizationOptions struct {
|
||||
// This allows us to configure the sleep time at each iteration and the maximum number of retries allowed
|
||||
// before we fail the webhook call in order to limit the fan out that ensues when the system is degraded.
|
||||
WebhookRetryBackoff *wait.Backoff
|
||||
|
||||
// AuthorizationConfigurationFile is mutually exclusive with all of:
|
||||
// - Modes
|
||||
// - WebhookConfigFile
|
||||
// - WebHookVersion
|
||||
// - WebhookCacheAuthorizedTTL
|
||||
// - WebhookCacheUnauthorizedTTL
|
||||
AuthorizationConfigurationFile string
|
||||
|
||||
AreLegacyFlagsSet func() bool
|
||||
}
|
||||
|
||||
// NewBuiltInAuthorizationOptions create a BuiltInAuthorizationOptions with default value
|
||||
@ -69,6 +94,54 @@ func (o *BuiltInAuthorizationOptions) Validate() []error {
|
||||
return nil
|
||||
}
|
||||
var allErrors []error
|
||||
|
||||
// if --authorization-config is set, check if
|
||||
// - the feature flag is set
|
||||
// - legacyFlags are not set
|
||||
// - the config file can be loaded
|
||||
// - the config file represents a valid configuration
|
||||
if o.AuthorizationConfigurationFile != "" {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StructuredAuthorizationConfiguration) {
|
||||
return append(allErrors, fmt.Errorf("--%s cannot be used without enabling StructuredAuthorizationConfiguration feature flag", authorizationConfigFlag))
|
||||
}
|
||||
|
||||
// error out if legacy flags are defined
|
||||
if o.AreLegacyFlagsSet != nil && o.AreLegacyFlagsSet() {
|
||||
return append(allErrors, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag))
|
||||
}
|
||||
|
||||
// load the file and check for errors
|
||||
config, err := load.LoadFromFile(o.AuthorizationConfigurationFile)
|
||||
if err != nil {
|
||||
return append(allErrors, fmt.Errorf("failed to load AuthorizationConfiguration from file: %v", err))
|
||||
}
|
||||
|
||||
// validate the file and return any error
|
||||
if errors := validation.ValidateAuthorizationConfiguration(nil, config,
|
||||
sets.NewString(authzmodes.AuthorizationModeChoices...),
|
||||
sets.NewString(repeatableAuthorizerTypes...),
|
||||
); len(errors) != 0 {
|
||||
allErrors = append(allErrors, errors.ToAggregate().Errors()...)
|
||||
}
|
||||
|
||||
// test to check if the authorizer names passed conform to the authorizers for type!=Webhook
|
||||
// this test is only for kube-apiserver and hence checked here
|
||||
// it preserves compatibility with o.buildAuthorizationConfiguration
|
||||
for _, authorizer := range config.Authorizers {
|
||||
if string(authorizer.Type) == authzmodes.ModeWebhook {
|
||||
continue
|
||||
}
|
||||
|
||||
expectedName := getNameForAuthorizerMode(string(authorizer.Type))
|
||||
if expectedName != authorizer.Name {
|
||||
allErrors = append(allErrors, fmt.Errorf("expected name %s for authorizer %s instead of %s", expectedName, authorizer.Type, authorizer.Name))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
// validate the legacy flags using the legacy mode if --authorization-config is not passed
|
||||
if len(o.Modes) == 0 {
|
||||
allErrors = append(allErrors, fmt.Errorf("at least one authorization-mode must be passed"))
|
||||
}
|
||||
@ -111,27 +184,47 @@ func (o *BuiltInAuthorizationOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringSliceVar(&o.Modes, "authorization-mode", o.Modes, ""+
|
||||
fs.StringSliceVar(&o.Modes, authorizationModeFlag, o.Modes, ""+
|
||||
"Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+
|
||||
strings.Join(authzmodes.AuthorizationModeChoices, ",")+".")
|
||||
|
||||
fs.StringVar(&o.PolicyFile, "authorization-policy-file", o.PolicyFile, ""+
|
||||
fs.StringVar(&o.PolicyFile, authorizationPolicyFileFlag, o.PolicyFile, ""+
|
||||
"File with authorization policy in json line by line format, used with --authorization-mode=ABAC, on the secure port.")
|
||||
|
||||
fs.StringVar(&o.WebhookConfigFile, "authorization-webhook-config-file", o.WebhookConfigFile, ""+
|
||||
fs.StringVar(&o.WebhookConfigFile, authorizationWebhookConfigFileFlag, o.WebhookConfigFile, ""+
|
||||
"File with webhook configuration in kubeconfig format, used with --authorization-mode=Webhook. "+
|
||||
"The API server will query the remote service to determine access on the API server's secure port.")
|
||||
|
||||
fs.StringVar(&o.WebhookVersion, "authorization-webhook-version", o.WebhookVersion, ""+
|
||||
fs.StringVar(&o.WebhookVersion, authorizationWebhookVersionFlag, o.WebhookVersion, ""+
|
||||
"The API version of the authorization.k8s.io SubjectAccessReview to send to and expect from the webhook.")
|
||||
|
||||
fs.DurationVar(&o.WebhookCacheAuthorizedTTL, "authorization-webhook-cache-authorized-ttl",
|
||||
fs.DurationVar(&o.WebhookCacheAuthorizedTTL, authorizationWebhookAuthorizedTTLFlag,
|
||||
o.WebhookCacheAuthorizedTTL,
|
||||
"The duration to cache 'authorized' responses from the webhook authorizer.")
|
||||
|
||||
fs.DurationVar(&o.WebhookCacheUnauthorizedTTL,
|
||||
"authorization-webhook-cache-unauthorized-ttl", o.WebhookCacheUnauthorizedTTL,
|
||||
authorizationWebhookUnauthorizedTTLFlag, o.WebhookCacheUnauthorizedTTL,
|
||||
"The duration to cache 'unauthorized' responses from the webhook authorizer.")
|
||||
|
||||
fs.StringVar(&o.AuthorizationConfigurationFile, authorizationConfigFlag, o.AuthorizationConfigurationFile, ""+
|
||||
"File with Authorization Configuration to configure the authorizer chain."+
|
||||
"Note: This feature is in Alpha since v1.29."+
|
||||
"--feature-gate=StructuredAuthorizationConfiguration=true feature flag needs to be set to true for enabling the functionality."+
|
||||
"This feature is mutually exclusive with the other --authorization-mode and --authorization-webhook-* flags.")
|
||||
|
||||
// preserves compatibility with any method set during initialization
|
||||
oldAreLegacyFlagsSet := o.AreLegacyFlagsSet
|
||||
o.AreLegacyFlagsSet = func() bool {
|
||||
if oldAreLegacyFlagsSet != nil && oldAreLegacyFlagsSet() {
|
||||
return true
|
||||
}
|
||||
|
||||
return fs.Changed(authorizationModeFlag) ||
|
||||
fs.Changed(authorizationWebhookConfigFileFlag) ||
|
||||
fs.Changed(authorizationWebhookVersionFlag) ||
|
||||
fs.Changed(authorizationWebhookAuthorizedTTLFlag) ||
|
||||
fs.Changed(authorizationWebhookUnauthorizedTTLFlag)
|
||||
}
|
||||
}
|
||||
|
||||
// ToAuthorizationConfig convert BuiltInAuthorizationOptions to authorizer.Config
|
||||
@ -140,9 +233,44 @@ func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFac
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
authzConfiguration, err := o.buildAuthorizationConfiguration()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build authorization config: %s", err)
|
||||
var authorizationConfiguration *authzconfig.AuthorizationConfiguration
|
||||
var err error
|
||||
|
||||
// if --authorization-config is set, check if
|
||||
// - the feature flag is set
|
||||
// - legacyFlags are not set
|
||||
// - the config file can be loaded
|
||||
// - the config file represents a valid configuration
|
||||
// else,
|
||||
// - build the AuthorizationConfig from the legacy flags
|
||||
if o.AuthorizationConfigurationFile != "" {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StructuredAuthorizationConfiguration) {
|
||||
return nil, fmt.Errorf("--%s cannot be used without enabling StructuredAuthorizationConfiguration feature flag", authorizationConfigFlag)
|
||||
}
|
||||
|
||||
// error out if legacy flags are defined
|
||||
if o.AreLegacyFlagsSet != nil && o.AreLegacyFlagsSet() {
|
||||
return nil, fmt.Errorf("--%s can not be specified when --%s or --authorization-webhook-* flags are defined", authorizationConfigFlag, authorizationModeFlag)
|
||||
}
|
||||
|
||||
// load the file and check for errors
|
||||
authorizationConfiguration, err = load.LoadFromFile(o.AuthorizationConfigurationFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load AuthorizationConfiguration from file: %v", err)
|
||||
}
|
||||
|
||||
// validate the file and return any error
|
||||
if errors := validation.ValidateAuthorizationConfiguration(nil, authorizationConfiguration,
|
||||
sets.NewString(authzmodes.AuthorizationModeChoices...),
|
||||
sets.NewString(repeatableAuthorizerTypes...),
|
||||
); len(errors) != 0 {
|
||||
return nil, fmt.Errorf(errors.ToAggregate().Error())
|
||||
}
|
||||
} else {
|
||||
authorizationConfiguration, err = o.buildAuthorizationConfiguration()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to build authorization config: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return &authorizer.Config{
|
||||
@ -150,7 +278,7 @@ func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFac
|
||||
VersionedInformerFactory: versionedInformerFactory,
|
||||
WebhookRetryBackoff: o.WebhookRetryBackoff,
|
||||
|
||||
AuthorizationConfiguration: authzConfiguration,
|
||||
AuthorizationConfiguration: authorizationConfiguration,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -173,6 +173,13 @@ func TestBuiltInAuthorizationOptionsAddFlags(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if !opts.AreLegacyFlagsSet() {
|
||||
t.Fatal("legacy flags should have been configured")
|
||||
}
|
||||
|
||||
// setting the method to nil since methods can't be compared with reflect.DeepEqual
|
||||
opts.AreLegacyFlagsSet = nil
|
||||
|
||||
if !reflect.DeepEqual(opts, expected) {
|
||||
t.Error(cmp.Diff(opts, expected))
|
||||
}
|
||||
|
82
staging/src/k8s.io/apiserver/pkg/apis/apiserver/load/load.go
Normal file
82
staging/src/k8s.io/apiserver/pkg/apis/apiserver/load/load.go
Normal file
@ -0,0 +1,82 @@
|
||||
/*
|
||||
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 load
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
api "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver/install"
|
||||
externalapi "k8s.io/apiserver/pkg/apis/apiserver/v1alpha1"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
codecs = serializer.NewCodecFactory(scheme, serializer.EnableStrict)
|
||||
)
|
||||
|
||||
func init() {
|
||||
install.Install(scheme)
|
||||
}
|
||||
|
||||
func LoadFromFile(file string) (*api.AuthorizationConfiguration, error) {
|
||||
data, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return LoadFromData(data)
|
||||
}
|
||||
|
||||
func LoadFromReader(reader io.Reader) (*api.AuthorizationConfiguration, error) {
|
||||
if reader == nil {
|
||||
// no reader specified, use default config
|
||||
return LoadFromData(nil)
|
||||
}
|
||||
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return LoadFromData(data)
|
||||
}
|
||||
|
||||
func LoadFromData(data []byte) (*api.AuthorizationConfiguration, error) {
|
||||
if len(data) == 0 {
|
||||
// no config provided, return default
|
||||
externalConfig := &externalapi.AuthorizationConfiguration{}
|
||||
scheme.Default(externalConfig)
|
||||
internalConfig := &api.AuthorizationConfiguration{}
|
||||
if err := scheme.Convert(externalConfig, internalConfig, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return internalConfig, nil
|
||||
}
|
||||
|
||||
decodedObj, err := runtime.Decode(codecs.UniversalDecoder(), data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configuration, ok := decodedObj.(*api.AuthorizationConfiguration)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected AuthorizationConfiguration, got %T", decodedObj)
|
||||
}
|
||||
return configuration, nil
|
||||
}
|
@ -0,0 +1,290 @@
|
||||
/*
|
||||
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 load
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
api "k8s.io/apiserver/pkg/apis/apiserver"
|
||||
)
|
||||
|
||||
var defaultConfig = &api.AuthorizationConfiguration{}
|
||||
|
||||
func writeTempFile(t *testing.T, content string) string {
|
||||
t.Helper()
|
||||
file, err := os.CreateTemp("", "config")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
if err := os.Remove(file.Name()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
if err := os.WriteFile(file.Name(), []byte(content), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return file.Name()
|
||||
}
|
||||
|
||||
func TestLoadFromFile(t *testing.T) {
|
||||
// no file
|
||||
{
|
||||
_, err := LoadFromFile("")
|
||||
if err == nil {
|
||||
t.Fatalf("expected err: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// empty file
|
||||
{
|
||||
config, err := LoadFromFile(writeTempFile(t, ``))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, defaultConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(defaultConfig, config))
|
||||
}
|
||||
}
|
||||
|
||||
// valid file
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"apiserver.config.k8s.io/v1alpha1",
|
||||
"kind":"AuthorizationConfiguration",
|
||||
"authorizers":[{"type":"Webhook"}]}`
|
||||
expect := &api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
|
||||
}
|
||||
|
||||
config, err := LoadFromFile(writeTempFile(t, input))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, expect) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(expect, config))
|
||||
}
|
||||
}
|
||||
|
||||
// missing file
|
||||
{
|
||||
_, err := LoadFromFile(`bogus-missing-file`)
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "bogus-missing-file") {
|
||||
t.Fatalf("expected missing file error, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// invalid content file
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"apiserver.config.k8s.io/v99",
|
||||
"kind":"AuthorizationConfiguration",
|
||||
"authorizers":{"type":"Webhook"}}`
|
||||
|
||||
_, err := LoadFromFile(writeTempFile(t, input))
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "apiserver.config.k8s.io/v99") {
|
||||
t.Fatalf("expected apiVersion error, got %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromReader(t *testing.T) {
|
||||
// no reader
|
||||
{
|
||||
config, err := LoadFromReader(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, defaultConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(defaultConfig, config))
|
||||
}
|
||||
}
|
||||
|
||||
// empty reader
|
||||
{
|
||||
config, err := LoadFromReader(&bytes.Buffer{})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, defaultConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(defaultConfig, config))
|
||||
}
|
||||
}
|
||||
|
||||
// valid reader
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"apiserver.config.k8s.io/v1alpha1",
|
||||
"kind":"AuthorizationConfiguration",
|
||||
"authorizers":[{"type":"Webhook"}]}`
|
||||
expect := &api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
|
||||
}
|
||||
|
||||
config, err := LoadFromReader(bytes.NewBufferString(input))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected err: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, expect) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(expect, config))
|
||||
}
|
||||
}
|
||||
|
||||
// invalid reader
|
||||
{
|
||||
input := `{
|
||||
"apiVersion":"apiserver.config.k8s.io/v99",
|
||||
"kind":"AuthorizationConfiguration",
|
||||
"authorizers":[{"type":"Webhook"}]}`
|
||||
|
||||
_, err := LoadFromReader(bytes.NewBufferString(input))
|
||||
if err == nil {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "apiserver.config.k8s.io/v99") {
|
||||
t.Fatalf("expected apiVersion error, got %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFromData(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
data []byte
|
||||
expectErr string
|
||||
expectConfig *api.AuthorizationConfiguration
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
data: nil,
|
||||
expectConfig: defaultConfig,
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
data: []byte{},
|
||||
expectConfig: defaultConfig,
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 - json",
|
||||
data: []byte(`{
|
||||
"apiVersion":"apiserver.config.k8s.io/v1alpha1",
|
||||
"kind":"AuthorizationConfiguration",
|
||||
"authorizers":[{"type":"Webhook"}]}`),
|
||||
expectConfig: &api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 - defaults",
|
||||
data: []byte(`{
|
||||
"apiVersion":"apiserver.config.k8s.io/v1alpha1",
|
||||
"kind":"AuthorizationConfiguration",
|
||||
"authorizers":[{"type":"Webhook","name":"default","webhook":{}}]}`),
|
||||
expectConfig: &api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{{
|
||||
Type: "Webhook",
|
||||
Name: "default",
|
||||
Webhook: &api.WebhookConfiguration{
|
||||
AuthorizedTTL: metav1.Duration{Duration: 5 * time.Minute},
|
||||
UnauthorizedTTL: metav1.Duration{Duration: 30 * time.Second},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "v1alpha1 - yaml",
|
||||
data: []byte(`
|
||||
apiVersion: apiserver.config.k8s.io/v1alpha1
|
||||
kind: AuthorizationConfiguration
|
||||
authorizers:
|
||||
- type: Webhook
|
||||
`),
|
||||
expectConfig: &api.AuthorizationConfiguration{
|
||||
Authorizers: []api.AuthorizerConfiguration{{Type: "Webhook"}},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "missing apiVersion",
|
||||
data: []byte(`{"kind":"AuthorizationConfiguration"}`),
|
||||
expectErr: `'apiVersion' is missing`,
|
||||
},
|
||||
{
|
||||
name: "missing kind",
|
||||
data: []byte(`{"apiVersion":"apiserver.config.k8s.io/v1alpha1"}`),
|
||||
expectErr: `'Kind' is missing`,
|
||||
},
|
||||
{
|
||||
name: "unknown group",
|
||||
data: []byte(`{"apiVersion":"apps/v1alpha1","kind":"AuthorizationConfiguration"}`),
|
||||
expectErr: `apps/v1alpha1`,
|
||||
},
|
||||
{
|
||||
name: "unknown version",
|
||||
data: []byte(`{"apiVersion":"apiserver.config.k8s.io/v99","kind":"AuthorizationConfiguration"}`),
|
||||
expectErr: `apiserver.config.k8s.io/v99`,
|
||||
},
|
||||
{
|
||||
name: "unknown kind",
|
||||
data: []byte(`{"apiVersion":"apiserver.config.k8s.io/v1alpha1","kind":"SomeConfiguration"}`),
|
||||
expectErr: `SomeConfiguration`,
|
||||
},
|
||||
{
|
||||
name: "unknown field",
|
||||
data: []byte(`{
|
||||
"apiVersion":"apiserver.config.k8s.io/v1alpha1",
|
||||
"kind":"AuthorizationConfiguration",
|
||||
"authorzers":[{"type":"Webhook"}]}`),
|
||||
expectErr: `unknown field "authorzers"`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
config, err := LoadFromData(tc.data)
|
||||
if err != nil {
|
||||
if len(tc.expectErr) == 0 {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tc.expectErr) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(tc.expectErr) > 0 {
|
||||
t.Fatalf("expected err, got none")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(config, tc.expectConfig) {
|
||||
t.Fatalf("unexpected config:\n%s", cmp.Diff(tc.expectConfig, config))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -1457,6 +1457,7 @@ k8s.io/apiserver/pkg/admission/plugin/webhook/validating
|
||||
k8s.io/apiserver/pkg/admission/testing
|
||||
k8s.io/apiserver/pkg/apis/apiserver
|
||||
k8s.io/apiserver/pkg/apis/apiserver/install
|
||||
k8s.io/apiserver/pkg/apis/apiserver/load
|
||||
k8s.io/apiserver/pkg/apis/apiserver/v1
|
||||
k8s.io/apiserver/pkg/apis/apiserver/v1alpha1
|
||||
k8s.io/apiserver/pkg/apis/apiserver/v1beta1
|
||||
|
Loading…
Reference in New Issue
Block a user