Add namespace scoped ParametersReference to IngressClass
This commit is contained in:
@@ -313,7 +313,42 @@ type IngressClassSpec struct {
|
||||
// configuration for the controller. This is optional if the controller does
|
||||
// not require extra parameters.
|
||||
// +optional
|
||||
Parameters *api.TypedLocalObjectReference
|
||||
Parameters *IngressClassParametersReference
|
||||
}
|
||||
|
||||
const (
|
||||
// IngressClassParametersReferenceScopeNamespace indicates that the
|
||||
// referenced Parameters resource is namespace-scoped.
|
||||
IngressClassParametersReferenceScopeNamespace = "Namespace"
|
||||
// IngressClassParametersReferenceScopeNamespace indicates that the
|
||||
// referenced Parameters resource is cluster-scoped.
|
||||
IngressClassParametersReferenceScopeCluster = "Cluster"
|
||||
)
|
||||
|
||||
// IngressClassParametersReference identifies an API object. This can be used
|
||||
// to specify a cluster or namespace-scoped resource.
|
||||
type IngressClassParametersReference struct {
|
||||
// APIGroup is the group for the resource being referenced. If APIGroup is
|
||||
// not specified, the specified Kind must be in the core API group. For any
|
||||
// other third-party types, APIGroup is required.
|
||||
// +optional
|
||||
APIGroup *string
|
||||
// Kind is the type of resource being referenced.
|
||||
Kind string
|
||||
// Name is the name of resource being referenced.
|
||||
Name string
|
||||
// Scope represents if this refers to a cluster or namespace scoped resource.
|
||||
// This may be set to "Cluster" (default) or "Namespace".
|
||||
// Field can be enabled with IngressClassNamespacedParams feature gate.
|
||||
// +optional
|
||||
// +featureGate=IngressClassNamespacedParams
|
||||
Scope *string
|
||||
// Namespace is the namespace of the resource being referenced. This field is
|
||||
// required when scope is set to "Namespace" and must be unset when scope is set to
|
||||
// "Cluster".
|
||||
// +optional
|
||||
// +featureGate=IngressClassNamespacedParams
|
||||
Namespace *string
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
@@ -20,6 +20,9 @@ import (
|
||||
"k8s.io/api/core/v1"
|
||||
networkingv1 "k8s.io/api/networking/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func addDefaultingFuncs(scheme *runtime.Scheme) error {
|
||||
@@ -43,3 +46,12 @@ func SetDefaults_NetworkPolicy(obj *networkingv1.NetworkPolicy) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SetDefaults_IngressClass(obj *networkingv1.IngressClass) {
|
||||
if !utilfeature.DefaultFeatureGate.Enabled(features.IngressClassNamespacedParams) {
|
||||
return
|
||||
}
|
||||
if obj.Spec.Parameters != nil && obj.Spec.Parameters.Scope == nil {
|
||||
obj.Spec.Parameters.Scope = utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeCluster)
|
||||
}
|
||||
}
|
||||
|
@@ -24,10 +24,14 @@ import (
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||
_ "k8s.io/kubernetes/pkg/apis/core/install"
|
||||
_ "k8s.io/kubernetes/pkg/apis/networking/install"
|
||||
. "k8s.io/kubernetes/pkg/apis/networking/v1"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
func TestSetDefaultNetworkPolicy(t *testing.T) {
|
||||
@@ -234,6 +238,132 @@ func TestSetDefaultNetworkPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultsForIngressClassParametersReference(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
original *networkingv1.IngressClass
|
||||
expected *networkingv1.IngressClass
|
||||
enableNamespaceScopedParamsGate bool
|
||||
}{
|
||||
{
|
||||
name: "populated parameters sets the default Scope when feature is enabled",
|
||||
original: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
Parameters: &networkingv1.IngressClassParametersReference{
|
||||
Kind: "k",
|
||||
Name: "n",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
Parameters: &networkingv1.IngressClassParametersReference{
|
||||
Kind: "k",
|
||||
Name: "n",
|
||||
Scope: utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeCluster),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
},
|
||||
{
|
||||
name: "existing scope is not overridden when feature is enabled",
|
||||
original: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
Parameters: &networkingv1.IngressClassParametersReference{
|
||||
Kind: "k",
|
||||
Name: "n",
|
||||
Scope: utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeNamespace),
|
||||
Namespace: utilpointer.StringPtr("foo-ns"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
Parameters: &networkingv1.IngressClassParametersReference{
|
||||
Kind: "k",
|
||||
Name: "n",
|
||||
Scope: utilpointer.StringPtr(networkingv1.IngressClassParametersReferenceScopeNamespace),
|
||||
Namespace: utilpointer.StringPtr("foo-ns"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
},
|
||||
{
|
||||
name: "empty Parameters does not set the default Scope when feature is enabled",
|
||||
original: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
},
|
||||
},
|
||||
expected: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
},
|
||||
{
|
||||
name: "populated parameters does not set the default Scope when feature is disabled",
|
||||
original: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
Parameters: &networkingv1.IngressClassParametersReference{
|
||||
Kind: "k",
|
||||
Name: "n",
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
Parameters: &networkingv1.IngressClassParametersReference{
|
||||
Kind: "k",
|
||||
Name: "n",
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: false,
|
||||
},
|
||||
{
|
||||
name: "empty Parameters does not set the default Scope when feature is disabled",
|
||||
original: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
},
|
||||
},
|
||||
expected: &networkingv1.IngressClass{
|
||||
Spec: networkingv1.IngressClassSpec{
|
||||
Controller: "controller",
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
original := test.original
|
||||
expected := test.expected
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IngressClassNamespacedParams, test.enableNamespaceScopedParamsGate)()
|
||||
obj2 := roundTrip(t, runtime.Object(original))
|
||||
got, ok := obj2.(*networkingv1.IngressClass)
|
||||
if !ok {
|
||||
t.Errorf("unexpected object: %v", got)
|
||||
t.FailNow()
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) {
|
||||
t.Errorf("got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", got.Spec, expected.Spec)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
|
||||
t.Helper()
|
||||
data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(SchemeGroupVersion), obj)
|
||||
|
42
pkg/apis/networking/v1/zz_generated.conversion.go
generated
42
pkg/apis/networking/v1/zz_generated.conversion.go
generated
@@ -111,6 +111,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1.IngressClassParametersReference)(nil), (*networking.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(a.(*v1.IngressClassParametersReference), b.(*networking.IngressClassParametersReference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*networking.IngressClassParametersReference)(nil), (*v1.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(a.(*networking.IngressClassParametersReference), b.(*v1.IngressClassParametersReference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1.IngressClassSpec)(nil), (*networking.IngressClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1_IngressClassSpec_To_networking_IngressClassSpec(a.(*v1.IngressClassSpec), b.(*networking.IngressClassSpec), scope)
|
||||
}); err != nil {
|
||||
@@ -446,9 +456,37 @@ func Convert_networking_IngressClassList_To_v1_IngressClassList(in *networking.I
|
||||
return autoConvert_networking_IngressClassList_To_v1_IngressClassList(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
|
||||
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
|
||||
out.Kind = in.Kind
|
||||
out.Name = in.Name
|
||||
out.Scope = (*string)(unsafe.Pointer(in.Scope))
|
||||
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference is an autogenerated conversion function.
|
||||
func Convert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
|
||||
return autoConvert_v1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1.IngressClassParametersReference, s conversion.Scope) error {
|
||||
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
|
||||
out.Kind = in.Kind
|
||||
out.Name = in.Name
|
||||
out.Scope = (*string)(unsafe.Pointer(in.Scope))
|
||||
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference is an autogenerated conversion function.
|
||||
func Convert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1.IngressClassParametersReference, s conversion.Scope) error {
|
||||
return autoConvert_networking_IngressClassParametersReference_To_v1_IngressClassParametersReference(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1_IngressClassSpec_To_networking_IngressClassSpec(in *v1.IngressClassSpec, out *networking.IngressClassSpec, s conversion.Scope) error {
|
||||
out.Controller = in.Controller
|
||||
out.Parameters = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
|
||||
out.Parameters = (*networking.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -459,7 +497,7 @@ func Convert_v1_IngressClassSpec_To_networking_IngressClassSpec(in *v1.IngressCl
|
||||
|
||||
func autoConvert_networking_IngressClassSpec_To_v1_IngressClassSpec(in *networking.IngressClassSpec, out *v1.IngressClassSpec, s conversion.Scope) error {
|
||||
out.Controller = in.Controller
|
||||
out.Parameters = (*corev1.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
|
||||
out.Parameters = (*v1.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
13
pkg/apis/networking/v1/zz_generated.defaults.go
generated
13
pkg/apis/networking/v1/zz_generated.defaults.go
generated
@@ -29,11 +29,24 @@ import (
|
||||
// Public to allow building arbitrary schemes.
|
||||
// All generated defaulters are covering - they call all nested defaulters.
|
||||
func RegisterDefaults(scheme *runtime.Scheme) error {
|
||||
scheme.AddTypeDefaultingFunc(&v1.IngressClass{}, func(obj interface{}) { SetObjectDefaults_IngressClass(obj.(*v1.IngressClass)) })
|
||||
scheme.AddTypeDefaultingFunc(&v1.IngressClassList{}, func(obj interface{}) { SetObjectDefaults_IngressClassList(obj.(*v1.IngressClassList)) })
|
||||
scheme.AddTypeDefaultingFunc(&v1.NetworkPolicy{}, func(obj interface{}) { SetObjectDefaults_NetworkPolicy(obj.(*v1.NetworkPolicy)) })
|
||||
scheme.AddTypeDefaultingFunc(&v1.NetworkPolicyList{}, func(obj interface{}) { SetObjectDefaults_NetworkPolicyList(obj.(*v1.NetworkPolicyList)) })
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetObjectDefaults_IngressClass(in *v1.IngressClass) {
|
||||
SetDefaults_IngressClass(in)
|
||||
}
|
||||
|
||||
func SetObjectDefaults_IngressClassList(in *v1.IngressClassList) {
|
||||
for i := range in.Items {
|
||||
a := &in.Items[i]
|
||||
SetObjectDefaults_IngressClass(a)
|
||||
}
|
||||
}
|
||||
|
||||
func SetObjectDefaults_NetworkPolicy(in *v1.NetworkPolicy) {
|
||||
SetDefaults_NetworkPolicy(in)
|
||||
for i := range in.Spec.Ingress {
|
||||
|
@@ -89,6 +89,16 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1beta1.IngressClassParametersReference)(nil), (*networking.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(a.(*v1beta1.IngressClassParametersReference), b.(*networking.IngressClassParametersReference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*networking.IngressClassParametersReference)(nil), (*v1beta1.IngressClassParametersReference)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(a.(*networking.IngressClassParametersReference), b.(*v1beta1.IngressClassParametersReference), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*v1beta1.IngressClassSpec)(nil), (*networking.IngressClassSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(a.(*v1beta1.IngressClassSpec), b.(*networking.IngressClassSpec), scope)
|
||||
}); err != nil {
|
||||
@@ -333,9 +343,37 @@ func Convert_networking_IngressClassList_To_v1beta1_IngressClassList(in *network
|
||||
return autoConvert_networking_IngressClassList_To_v1beta1_IngressClassList(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1beta1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
|
||||
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
|
||||
out.Kind = in.Kind
|
||||
out.Name = in.Name
|
||||
out.Scope = (*string)(unsafe.Pointer(in.Scope))
|
||||
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference is an autogenerated conversion function.
|
||||
func Convert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in *v1beta1.IngressClassParametersReference, out *networking.IngressClassParametersReference, s conversion.Scope) error {
|
||||
return autoConvert_v1beta1_IngressClassParametersReference_To_networking_IngressClassParametersReference(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1beta1.IngressClassParametersReference, s conversion.Scope) error {
|
||||
out.APIGroup = (*string)(unsafe.Pointer(in.APIGroup))
|
||||
out.Kind = in.Kind
|
||||
out.Name = in.Name
|
||||
out.Scope = (*string)(unsafe.Pointer(in.Scope))
|
||||
out.Namespace = (*string)(unsafe.Pointer(in.Namespace))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference is an autogenerated conversion function.
|
||||
func Convert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(in *networking.IngressClassParametersReference, out *v1beta1.IngressClassParametersReference, s conversion.Scope) error {
|
||||
return autoConvert_networking_IngressClassParametersReference_To_v1beta1_IngressClassParametersReference(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(in *v1beta1.IngressClassSpec, out *networking.IngressClassSpec, s conversion.Scope) error {
|
||||
out.Controller = in.Controller
|
||||
out.Parameters = (*core.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
|
||||
out.Parameters = (*networking.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -346,7 +384,7 @@ func Convert_v1beta1_IngressClassSpec_To_networking_IngressClassSpec(in *v1beta1
|
||||
|
||||
func autoConvert_networking_IngressClassSpec_To_v1beta1_IngressClassSpec(in *networking.IngressClassSpec, out *v1beta1.IngressClassSpec, s conversion.Scope) error {
|
||||
out.Controller = in.Controller
|
||||
out.Parameters = (*v1.TypedLocalObjectReference)(unsafe.Pointer(in.Parameters))
|
||||
out.Parameters = (*v1beta1.IngressClassParametersReference)(unsafe.Pointer(in.Parameters))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -31,9 +31,12 @@ import (
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/util/validation"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||
"k8s.io/kubernetes/pkg/apis/networking"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -49,6 +52,11 @@ var (
|
||||
)
|
||||
invalidPathSequences = []string{"//", "/./", "/../", "%2f", "%2F"}
|
||||
invalidPathSuffixes = []string{"/..", "/."}
|
||||
|
||||
supportedIngressClassParametersReferenceScopes = sets.NewString(
|
||||
networking.IngressClassParametersReferenceScopeNamespace,
|
||||
networking.IngressClassParametersReferenceScopeCluster,
|
||||
)
|
||||
)
|
||||
|
||||
// ValidateNetworkPolicyName can be used to check whether the given networkpolicy
|
||||
@@ -518,7 +526,7 @@ func validateIngressClassSpec(spec *networking.IngressClassSpec, fldPath *field.
|
||||
allErrs = append(allErrs, field.TooLong(fldPath.Child("controller"), spec.Controller, maxLenIngressClassController))
|
||||
}
|
||||
allErrs = append(allErrs, validation.IsDomainPrefixedPath(fldPath.Child("controller"), spec.Controller)...)
|
||||
allErrs = append(allErrs, validateIngressTypedLocalObjectReference(spec.Parameters, fldPath.Child("parameters"))...)
|
||||
allErrs = append(allErrs, validateIngressClassParametersReference(spec.Parameters, fldPath.Child("parameters"))...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
@@ -561,6 +569,55 @@ func validateIngressTypedLocalObjectReference(params *api.TypedLocalObjectRefere
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// validateIngressClassParametersReference ensures that Parameters fields are valid.
|
||||
// Parameters was previously of type `TypedLocalObjectReference` and used
|
||||
// `validateIngressTypedLocalObjectReference()`. This function extends validation
|
||||
// for additional fields introduced for namespace-scoped references.
|
||||
func validateIngressClassParametersReference(params *networking.IngressClassParametersReference, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if params == nil {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
allErrs = append(allErrs, validateIngressTypedLocalObjectReference(&api.TypedLocalObjectReference{
|
||||
APIGroup: params.APIGroup,
|
||||
Kind: params.Kind,
|
||||
Name: params.Name,
|
||||
}, fldPath)...)
|
||||
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.IngressClassNamespacedParams) && params.Scope == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("scope"), ""))
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if params.Scope != nil || params.Namespace != nil {
|
||||
scope := utilpointer.StringPtrDerefOr(params.Scope, "")
|
||||
|
||||
if !supportedIngressClassParametersReferenceScopes.Has(scope) {
|
||||
allErrs = append(allErrs, field.NotSupported(fldPath.Child("scope"), scope,
|
||||
supportedIngressClassParametersReferenceScopes.List()))
|
||||
} else {
|
||||
|
||||
if scope == networking.IngressClassParametersReferenceScopeNamespace {
|
||||
if params.Namespace == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), "`parameters.scope` is set to 'Namespace'"))
|
||||
} else {
|
||||
for _, msg := range apivalidation.ValidateNamespaceName(*params.Namespace, false) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), *params.Namespace, msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if scope == networking.IngressClassParametersReferenceScopeCluster && params.Namespace != nil {
|
||||
allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "`parameters.scope` is set to 'Cluster'"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func allowInvalidSecretName(gv schema.GroupVersion, oldIngress *networking.Ingress) bool {
|
||||
if gv == networkingv1beta1.SchemeGroupVersion || gv == extensionsv1beta1.SchemeGroupVersion {
|
||||
// backwards compatibility with released API versions that allowed invalid names
|
||||
|
@@ -28,8 +28,11 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
api "k8s.io/kubernetes/pkg/apis/core"
|
||||
"k8s.io/kubernetes/pkg/apis/networking"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
utilpointer "k8s.io/utils/pointer"
|
||||
)
|
||||
|
||||
@@ -2239,8 +2242,9 @@ func TestValidateIngressUpdate(t *testing.T) {
|
||||
|
||||
func TestValidateIngressClass(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
ingressClass networking.IngressClass
|
||||
expectedErrs field.ErrorList
|
||||
ingressClass networking.IngressClass
|
||||
expectedErrs field.ErrorList
|
||||
enableNamespaceScopedParamsGate bool
|
||||
}{
|
||||
"valid name, valid controller": {
|
||||
ingressClass: networking.IngressClass{
|
||||
@@ -2292,7 +2296,7 @@ func TestValidateIngressClass(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &api.TypedLocalObjectReference{
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
APIGroup: utilpointer.StringPtr("example.com"),
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
@@ -2306,7 +2310,7 @@ func TestValidateIngressClass(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &api.TypedLocalObjectReference{
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
APIGroup: utilpointer.StringPtr("example.com"),
|
||||
Name: "bar",
|
||||
},
|
||||
@@ -2319,7 +2323,7 @@ func TestValidateIngressClass(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &api.TypedLocalObjectReference{
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
},
|
||||
},
|
||||
@@ -2331,7 +2335,7 @@ func TestValidateIngressClass(t *testing.T) {
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &api.TypedLocalObjectReference{
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo/",
|
||||
Name: "bar",
|
||||
},
|
||||
@@ -2339,10 +2343,173 @@ func TestValidateIngressClass(t *testing.T) {
|
||||
},
|
||||
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec.parameters.kind"), "foo/", "may not contain '/'")},
|
||||
},
|
||||
"valid name, valid controller, invalid params (bad scope)": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("bad-scope"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{field.NotSupported(field.NewPath("spec.parameters.scope"),
|
||||
"bad-scope", []string{"Cluster", "Namespace"})},
|
||||
},
|
||||
"valid name, valid controller, valid Namespace scope": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("Namespace"),
|
||||
Namespace: utilpointer.StringPtr("foo-ns"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{},
|
||||
},
|
||||
"valid name, valid controller, valid scope, invalid namespace": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("Namespace"),
|
||||
Namespace: utilpointer.StringPtr("foo_ns"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{field.Invalid(field.NewPath("spec.parameters.namespace"), "foo_ns",
|
||||
"a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-',"+
|
||||
" and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', "+
|
||||
"regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')")},
|
||||
},
|
||||
"valid name, valid controller, valid Cluster scope": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("Cluster"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{},
|
||||
},
|
||||
"namespace not set when scope is Namespace": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("Namespace"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.namespace"),
|
||||
"`parameters.scope` is set to 'Namespace'")},
|
||||
},
|
||||
"namespace is forbidden when scope is Cluster": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("Cluster"),
|
||||
Namespace: utilpointer.StringPtr("foo-ns"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{field.Forbidden(field.NewPath("spec.parameters.namespace"),
|
||||
"`parameters.scope` is set to 'Cluster'")},
|
||||
},
|
||||
"empty namespace is forbidden when scope is Cluster": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("Cluster"),
|
||||
Namespace: utilpointer.StringPtr(""),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{field.Forbidden(field.NewPath("spec.parameters.namespace"),
|
||||
"`parameters.scope` is set to 'Cluster'")},
|
||||
},
|
||||
"validation is performed when feature gate is disabled and scope is not empty": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Scope: utilpointer.StringPtr("bad-scope"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: false,
|
||||
expectedErrs: field.ErrorList{field.NotSupported(field.NewPath("spec.parameters.scope"),
|
||||
"bad-scope", []string{"Cluster", "Namespace"})},
|
||||
},
|
||||
"validation fails when feature gate is enabled and scope is not set": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: true,
|
||||
expectedErrs: field.ErrorList{field.Required(field.NewPath("spec.parameters.scope"), "")},
|
||||
},
|
||||
"validation is performed when feature gate is disabled and namespace is not empty": {
|
||||
ingressClass: networking.IngressClass{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "test123"},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
Kind: "foo",
|
||||
Name: "bar",
|
||||
Namespace: utilpointer.StringPtr("foo-ns"),
|
||||
},
|
||||
},
|
||||
},
|
||||
enableNamespaceScopedParamsGate: false,
|
||||
expectedErrs: field.ErrorList{field.NotSupported(field.NewPath("spec.parameters.scope"),
|
||||
"", []string{"Cluster", "Namespace"})},
|
||||
},
|
||||
}
|
||||
|
||||
for name, testCase := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.IngressClassNamespacedParams, testCase.enableNamespaceScopedParamsGate)()
|
||||
errs := ValidateIngressClass(&testCase.ingressClass)
|
||||
|
||||
if len(errs) != len(testCase.expectedErrs) {
|
||||
@@ -2390,7 +2557,7 @@ func TestValidateIngressClassUpdate(t *testing.T) {
|
||||
},
|
||||
Spec: networking.IngressClassSpec{
|
||||
Controller: "foo.co/bar",
|
||||
Parameters: &api.TypedLocalObjectReference{
|
||||
Parameters: &networking.IngressClassParametersReference{
|
||||
APIGroup: utilpointer.StringPtr("v1"),
|
||||
Kind: "ConfigMap",
|
||||
Name: "foo",
|
||||
|
33
pkg/apis/networking/zz_generated.deepcopy.go
generated
33
pkg/apis/networking/zz_generated.deepcopy.go
generated
@@ -207,12 +207,43 @@ func (in *IngressClassList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IngressClassParametersReference) DeepCopyInto(out *IngressClassParametersReference) {
|
||||
*out = *in
|
||||
if in.APIGroup != nil {
|
||||
in, out := &in.APIGroup, &out.APIGroup
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.Scope != nil {
|
||||
in, out := &in.Scope, &out.Scope
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.Namespace != nil {
|
||||
in, out := &in.Namespace, &out.Namespace
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressClassParametersReference.
|
||||
func (in *IngressClassParametersReference) DeepCopy() *IngressClassParametersReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(IngressClassParametersReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *IngressClassSpec) DeepCopyInto(out *IngressClassSpec) {
|
||||
*out = *in
|
||||
if in.Parameters != nil {
|
||||
in, out := &in.Parameters, &out.Parameters
|
||||
*out = new(core.TypedLocalObjectReference)
|
||||
*out = new(IngressClassParametersReference)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
return
|
||||
|
Reference in New Issue
Block a user