mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-04 12:18:16 +00:00 
			
		
		
		
	Add --authorization-config flag to apiserver
Signed-off-by: Nabarun Pal <pal.nabarun95@gmail.com>
This commit is contained in:
		@@ -327,6 +327,13 @@ func TestAddFlags(t *testing.T) {
 | 
				
			|||||||
	expected.Authentication.OIDC.UsernameClaim = "sub"
 | 
						expected.Authentication.OIDC.UsernameClaim = "sub"
 | 
				
			||||||
	expected.Authentication.OIDC.SigningAlgs = []string{"RS256"}
 | 
						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) {
 | 
						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{})))
 | 
							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.UsernameClaim = "sub"
 | 
				
			||||||
	expected.Authentication.OIDC.SigningAlgs = []string{"RS256"}
 | 
						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) {
 | 
						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{})))
 | 
							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"
 | 
						"strings"
 | 
				
			||||||
	"time"
 | 
						"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"
 | 
						"github.com/spf13/pflag"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/sets"
 | 
						"k8s.io/apimachinery/pkg/util/sets"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/wait"
 | 
						"k8s.io/apimachinery/pkg/util/wait"
 | 
				
			||||||
	authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
 | 
						authzconfig "k8s.io/apiserver/pkg/apis/apiserver"
 | 
				
			||||||
 | 
						"k8s.io/apiserver/pkg/apis/apiserver/validation"
 | 
				
			||||||
	genericoptions "k8s.io/apiserver/pkg/server/options"
 | 
						genericoptions "k8s.io/apiserver/pkg/server/options"
 | 
				
			||||||
	versionedinformers "k8s.io/client-go/informers"
 | 
						versionedinformers "k8s.io/client-go/informers"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,8 +41,18 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const (
 | 
					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
 | 
					// BuiltInAuthorizationOptions contains all build-in authorization options for API Server
 | 
				
			||||||
type BuiltInAuthorizationOptions struct {
 | 
					type BuiltInAuthorizationOptions struct {
 | 
				
			||||||
	Modes                       []string
 | 
						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
 | 
						// 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.
 | 
						// before we fail the webhook call in order to limit the fan out that ensues when the system is degraded.
 | 
				
			||||||
	WebhookRetryBackoff *wait.Backoff
 | 
						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
 | 
					// NewBuiltInAuthorizationOptions create a BuiltInAuthorizationOptions with default value
 | 
				
			||||||
@@ -69,6 +94,54 @@ func (o *BuiltInAuthorizationOptions) Validate() []error {
 | 
				
			|||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	var allErrors []error
 | 
						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 {
 | 
						if len(o.Modes) == 0 {
 | 
				
			||||||
		allErrors = append(allErrors, fmt.Errorf("at least one authorization-mode must be passed"))
 | 
							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
 | 
							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: "+
 | 
							"Ordered list of plug-ins to do authorization on secure port. Comma-delimited list of: "+
 | 
				
			||||||
		strings.Join(authzmodes.AuthorizationModeChoices, ",")+".")
 | 
							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.")
 | 
							"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. "+
 | 
							"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.")
 | 
							"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.")
 | 
							"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,
 | 
							o.WebhookCacheAuthorizedTTL,
 | 
				
			||||||
		"The duration to cache 'authorized' responses from the webhook authorizer.")
 | 
							"The duration to cache 'authorized' responses from the webhook authorizer.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fs.DurationVar(&o.WebhookCacheUnauthorizedTTL,
 | 
						fs.DurationVar(&o.WebhookCacheUnauthorizedTTL,
 | 
				
			||||||
		"authorization-webhook-cache-unauthorized-ttl", o.WebhookCacheUnauthorizedTTL,
 | 
							authorizationWebhookUnauthorizedTTLFlag, o.WebhookCacheUnauthorizedTTL,
 | 
				
			||||||
		"The duration to cache 'unauthorized' responses from the webhook authorizer.")
 | 
							"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
 | 
					// ToAuthorizationConfig convert BuiltInAuthorizationOptions to authorizer.Config
 | 
				
			||||||
@@ -140,17 +233,52 @@ func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFac
 | 
				
			|||||||
		return nil, nil
 | 
							return nil, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	authzConfiguration, err := o.buildAuthorizationConfiguration()
 | 
						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 {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("failed to build authorization config: %s", err)
 | 
								return nil, fmt.Errorf("failed to build authorization config: %s", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &authorizer.Config{
 | 
						return &authorizer.Config{
 | 
				
			||||||
		PolicyFile:               o.PolicyFile,
 | 
							PolicyFile:               o.PolicyFile,
 | 
				
			||||||
		VersionedInformerFactory: versionedInformerFactory,
 | 
							VersionedInformerFactory: versionedInformerFactory,
 | 
				
			||||||
		WebhookRetryBackoff:      o.WebhookRetryBackoff,
 | 
							WebhookRetryBackoff:      o.WebhookRetryBackoff,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		AuthorizationConfiguration: authzConfiguration,
 | 
							AuthorizationConfiguration: authorizationConfiguration,
 | 
				
			||||||
	}, nil
 | 
						}, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -173,6 +173,13 @@ func TestBuiltInAuthorizationOptionsAddFlags(t *testing.T) {
 | 
				
			|||||||
		t.Fatal(err)
 | 
							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) {
 | 
						if !reflect.DeepEqual(opts, expected) {
 | 
				
			||||||
		t.Error(cmp.Diff(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/admission/testing
 | 
				
			||||||
k8s.io/apiserver/pkg/apis/apiserver
 | 
					k8s.io/apiserver/pkg/apis/apiserver
 | 
				
			||||||
k8s.io/apiserver/pkg/apis/apiserver/install
 | 
					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/v1
 | 
				
			||||||
k8s.io/apiserver/pkg/apis/apiserver/v1alpha1
 | 
					k8s.io/apiserver/pkg/apis/apiserver/v1alpha1
 | 
				
			||||||
k8s.io/apiserver/pkg/apis/apiserver/v1beta1
 | 
					k8s.io/apiserver/pkg/apis/apiserver/v1beta1
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user