Promote resource quota admission configuration to v1

This commit is contained in:
Jordan Liggitt 2019-11-11 12:46:48 -05:00
parent 6d0994fa66
commit 7d3012f297
16 changed files with 616 additions and 17 deletions

View File

@ -244,6 +244,7 @@ pkg/volume/util/volumepathhandler
pkg/volume/vsphere_volume
plugin/pkg/admission/eventratelimit/apis/eventratelimit/v1alpha1
plugin/pkg/admission/limitranger
plugin/pkg/admission/resourcequota/apis/resourcequota/v1
plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1
plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1
plugin/pkg/auth/authorizer/node

View File

@ -22,7 +22,7 @@ go_library(
"//pkg/quota/v1/generic:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/install:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/validation:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
@ -49,7 +49,10 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["admission_test.go"],
srcs = [
"admission_test.go",
"config_test.go",
],
embed = [":go_default_library"],
deps = [
"//pkg/apis/core:go_default_library",

View File

@ -34,6 +34,7 @@ filegroup(
srcs = [
":package-srcs",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/install:all-srcs",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1:all-srcs",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1:all-srcs",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1:all-srcs",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/validation:all-srcs",

View File

@ -11,6 +11,7 @@ go_library(
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/install",
deps = [
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1:go_default_library",
"//plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",

View File

@ -22,6 +22,7 @@ import (
"k8s.io/apimachinery/pkg/runtime"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
resourcequotav1 "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1"
resourcequotav1alpha1 "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1"
resourcequotav1beta1 "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1"
)
@ -29,7 +30,13 @@ import (
// Install registers the API group and adds types to a scheme
func Install(scheme *runtime.Scheme) {
utilruntime.Must(resourcequotaapi.AddToScheme(scheme))
// v1beta1 and v1alpha1 are in the k8s.io-suffixed group
utilruntime.Must(resourcequotav1beta1.AddToScheme(scheme))
utilruntime.Must(resourcequotav1alpha1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(resourcequotav1beta1.SchemeGroupVersion, resourcequotav1alpha1.SchemeGroupVersion))
// v1 is in the config.k8s.io-suffixed group
utilruntime.Must(resourcequotav1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(resourcequotav1.SchemeGroupVersion))
}

View File

@ -28,25 +28,23 @@ var (
AddToScheme = SchemeBuilder.AddToScheme
)
// LegacyGroupName is the group name use in this package
const LegacyGroupName = "resourcequota.admission.k8s.io"
// LegacySchemeGroupVersion is group version used to register these objects
var LegacySchemeGroupVersion = schema.GroupVersion{Group: LegacyGroupName, Version: runtime.APIVersionInternal}
// GroupName is the group name use in this package
const GroupName = "resourcequota.admission.k8s.io"
const GroupName = "apiserver.config.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
func addKnownTypes(scheme *runtime.Scheme) error {
// TODO this will get cleaned up with the scheme types are fixed
scheme.AddKnownTypes(SchemeGroupVersion,
scheme.AddKnownTypes(LegacySchemeGroupVersion,
&Configuration{},
)
scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ResourceQuotaConfiguration"),
&Configuration{},
)
return nil

View File

@ -0,0 +1,41 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"defaults.go",
"doc.go",
"register.go",
"types.go",
"zz_generated.conversion.go",
"zz_generated.deepcopy.go",
"zz_generated.defaults.go",
],
importpath = "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1",
deps = [
"//plugin/pkg/admission/resourcequota/apis/resourcequota:go_default_library",
"//staging/src/k8s.io/api/core/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)

View File

@ -0,0 +1,25 @@
/*
Copyright 2019 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 v1
import kruntime "k8s.io/apimachinery/pkg/runtime"
func addDefaultingFuncs(scheme *kruntime.Scheme) error {
return RegisterDefaults(scheme)
}
func SetDefaults_Configuration(obj *Configuration) {}

View File

@ -0,0 +1,23 @@
/*
Copyright 2019 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.
*/
// +k8s:deepcopy-gen=package
// +k8s:conversion-gen=k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota
// +k8s:defaulter-gen=TypeMeta
// +groupName=resourcequota.admission.k8s.io
// Package v1 is the v1 version of the API.
package v1 // import "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1"

View File

@ -0,0 +1,51 @@
/*
Copyright 2019 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 v1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package
const GroupName = "apiserver.config.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
var (
// TODO: move SchemeBuilder with zz_generated.deepcopy.go to k8s.io/api.
// localSchemeBuilder and AddToScheme will stay in k8s.io/kubernetes.
// SchemeBuilder is a pointer used to call AddToScheme
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
// AddToScheme is used to register the types to API encoding/decoding machinery
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
}
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypeWithName(SchemeGroupVersion.WithKind("ResourceQuotaConfiguration"), &Configuration{})
return nil
}

View File

@ -0,0 +1,69 @@
/*
Copyright 2019 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 v1
import (
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// Configuration provides configuration for the ResourceQuota admission controller.
type Configuration struct {
metav1.TypeMeta `json:",inline"`
// LimitedResources whose consumption is limited by default.
// +optional
LimitedResources []LimitedResource `json:"limitedResources"`
}
// LimitedResource matches a resource whose consumption is limited by default.
// To consume the resource, there must exist an associated quota that limits
// its consumption.
type LimitedResource struct {
// APIGroup is the name of the APIGroup that contains the limited resource.
// +optional
APIGroup string `json:"apiGroup,omitempty"`
// Resource is the name of the resource this rule applies to.
// For example, if the administrator wants to limit consumption
// of a storage resource associated with persistent volume claims,
// the value would be "persistentvolumeclaims".
Resource string `json:"resource"`
// For each intercepted request, the quota system will evaluate
// its resource usage. It will iterate through each resource consumed
// and if the resource contains any substring in this listing, the
// quota system will ensure that there is a covering quota. In the
// absence of a covering quota, the quota system will deny the request.
// For example, if an administrator wants to globally enforce that
// that a quota must exist to consume persistent volume claims associated
// with any storage class, the list would include
// ".storageclass.storage.k8s.io/requests.storage"
MatchContains []string `json:"matchContains,omitempty"`
// For each intercepted request, the quota system will figure out if the input object
// satisfies a scope which is present in this listing, then
// quota system will ensure that there is a covering quota. In the
// absence of a covering quota, the quota system will deny the request.
// For example, if an administrator wants to globally enforce that
// a quota must exist to create a pod with "cluster-services" priorityclass
// the list would include "scopeName=PriorityClass, Operator=In, Value=cluster-services"
// +optional
MatchScopes []v1.ScopedResourceSelectorRequirement `json:"matchScopes,omitempty"`
}

View File

@ -0,0 +1,106 @@
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by conversion-gen. DO NOT EDIT.
package v1
import (
unsafe "unsafe"
corev1 "k8s.io/api/core/v1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
resourcequota "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*Configuration)(nil), (*resourcequota.Configuration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_Configuration_To_resourcequota_Configuration(a.(*Configuration), b.(*resourcequota.Configuration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcequota.Configuration)(nil), (*Configuration)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resourcequota_Configuration_To_v1_Configuration(a.(*resourcequota.Configuration), b.(*Configuration), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*LimitedResource)(nil), (*resourcequota.LimitedResource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1_LimitedResource_To_resourcequota_LimitedResource(a.(*LimitedResource), b.(*resourcequota.LimitedResource), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*resourcequota.LimitedResource)(nil), (*LimitedResource)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_resourcequota_LimitedResource_To_v1_LimitedResource(a.(*resourcequota.LimitedResource), b.(*LimitedResource), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1_Configuration_To_resourcequota_Configuration(in *Configuration, out *resourcequota.Configuration, s conversion.Scope) error {
out.LimitedResources = *(*[]resourcequota.LimitedResource)(unsafe.Pointer(&in.LimitedResources))
return nil
}
// Convert_v1_Configuration_To_resourcequota_Configuration is an autogenerated conversion function.
func Convert_v1_Configuration_To_resourcequota_Configuration(in *Configuration, out *resourcequota.Configuration, s conversion.Scope) error {
return autoConvert_v1_Configuration_To_resourcequota_Configuration(in, out, s)
}
func autoConvert_resourcequota_Configuration_To_v1_Configuration(in *resourcequota.Configuration, out *Configuration, s conversion.Scope) error {
out.LimitedResources = *(*[]LimitedResource)(unsafe.Pointer(&in.LimitedResources))
return nil
}
// Convert_resourcequota_Configuration_To_v1_Configuration is an autogenerated conversion function.
func Convert_resourcequota_Configuration_To_v1_Configuration(in *resourcequota.Configuration, out *Configuration, s conversion.Scope) error {
return autoConvert_resourcequota_Configuration_To_v1_Configuration(in, out, s)
}
func autoConvert_v1_LimitedResource_To_resourcequota_LimitedResource(in *LimitedResource, out *resourcequota.LimitedResource, s conversion.Scope) error {
out.APIGroup = in.APIGroup
out.Resource = in.Resource
out.MatchContains = *(*[]string)(unsafe.Pointer(&in.MatchContains))
out.MatchScopes = *(*[]corev1.ScopedResourceSelectorRequirement)(unsafe.Pointer(&in.MatchScopes))
return nil
}
// Convert_v1_LimitedResource_To_resourcequota_LimitedResource is an autogenerated conversion function.
func Convert_v1_LimitedResource_To_resourcequota_LimitedResource(in *LimitedResource, out *resourcequota.LimitedResource, s conversion.Scope) error {
return autoConvert_v1_LimitedResource_To_resourcequota_LimitedResource(in, out, s)
}
func autoConvert_resourcequota_LimitedResource_To_v1_LimitedResource(in *resourcequota.LimitedResource, out *LimitedResource, s conversion.Scope) error {
out.APIGroup = in.APIGroup
out.Resource = in.Resource
out.MatchContains = *(*[]string)(unsafe.Pointer(&in.MatchContains))
out.MatchScopes = *(*[]corev1.ScopedResourceSelectorRequirement)(unsafe.Pointer(&in.MatchScopes))
return nil
}
// Convert_resourcequota_LimitedResource_To_v1_LimitedResource is an autogenerated conversion function.
func Convert_resourcequota_LimitedResource_To_v1_LimitedResource(in *resourcequota.LimitedResource, out *LimitedResource, s conversion.Scope) error {
return autoConvert_resourcequota_LimitedResource_To_v1_LimitedResource(in, out, s)
}

View File

@ -0,0 +1,86 @@
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by deepcopy-gen. DO NOT EDIT.
package v1
import (
corev1 "k8s.io/api/core/v1"
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Configuration) DeepCopyInto(out *Configuration) {
*out = *in
out.TypeMeta = in.TypeMeta
if in.LimitedResources != nil {
in, out := &in.LimitedResources, &out.LimitedResources
*out = make([]LimitedResource, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Configuration.
func (in *Configuration) DeepCopy() *Configuration {
if in == nil {
return nil
}
out := new(Configuration)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *Configuration) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *LimitedResource) DeepCopyInto(out *LimitedResource) {
*out = *in
if in.MatchContains != nil {
in, out := &in.MatchContains, &out.MatchContains
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.MatchScopes != nil {
in, out := &in.MatchScopes, &out.MatchScopes
*out = make([]corev1.ScopedResourceSelectorRequirement, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LimitedResource.
func (in *LimitedResource) DeepCopy() *LimitedResource {
if in == nil {
return nil
}
out := new(LimitedResource)
in.DeepCopyInto(out)
return out
}

View File

@ -0,0 +1,37 @@
// +build !ignore_autogenerated
/*
Copyright 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.
*/
// Code generated by defaulter-gen. DO NOT EDIT.
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
scheme.AddTypeDefaultingFunc(&Configuration{}, func(obj interface{}) { SetObjectDefaults_Configuration(obj.(*Configuration)) })
return nil
}
func SetObjectDefaults_Configuration(in *Configuration) {
SetDefaults_Configuration(in)
}

View File

@ -25,7 +25,7 @@ import (
"k8s.io/apimachinery/pkg/runtime/serializer"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
"k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/install"
resourcequotav1beta1 "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1beta1"
resourcequotav1 "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1"
)
var (
@ -41,7 +41,7 @@ func init() {
func LoadConfiguration(config io.Reader) (*resourcequotaapi.Configuration, error) {
// if no config is provided, return a default configuration
if config == nil {
externalConfig := &resourcequotav1beta1.Configuration{}
externalConfig := &resourcequotav1.Configuration{}
scheme.Default(externalConfig)
internalConfig := &resourcequotaapi.Configuration{}
if err := scheme.Convert(externalConfig, internalConfig, nil); err != nil {

View File

@ -0,0 +1,150 @@
/*
Copyright 2019 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 resourcequota
import (
"bytes"
"reflect"
"strings"
"testing"
corev1 "k8s.io/api/core/v1"
resourcequotaapi "k8s.io/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota"
)
func TestLoadConfiguration(t *testing.T) {
testcases := []struct {
name string
input string
expectErr string
expectConfig *resourcequotaapi.Configuration
}{
{
name: "empty",
input: ``,
expectErr: `'Kind' is missing`,
},
{
name: "unknown type",
input: `{"kind":"Unknown","apiVersion":"v1"}`,
expectErr: `no kind "Unknown" is registered`,
},
{
name: "valid v1alpha1 config",
input: `
kind: Configuration
apiVersion: resourcequota.admission.k8s.io/v1alpha1
limitedResources:
- apiGroup: ""
resource: persistentvolumeclaims
matchContains:
- .storageclass.storage.k8s.io/requests.storage
- apiGroup: ""
resource: pods
matchScopes:
- scopeName: PriorityClass
operator: In
values:
- cluster-services
`,
expectConfig: &resourcequotaapi.Configuration{
LimitedResources: []resourcequotaapi.LimitedResource{
{APIGroup: "", Resource: "persistentvolumeclaims", MatchContains: []string{".storageclass.storage.k8s.io/requests.storage"}},
{APIGroup: "", Resource: "pods", MatchScopes: []corev1.ScopedResourceSelectorRequirement{
{ScopeName: "PriorityClass", Operator: "In", Values: []string{"cluster-services"}},
},
},
}},
},
{
name: "valid v1beta1 config",
input: `
kind: Configuration
apiVersion: resourcequota.admission.k8s.io/v1beta1
limitedResources:
- apiGroup: ""
resource: persistentvolumeclaims
matchContains:
- .storageclass.storage.k8s.io/requests.storage
- apiGroup: ""
resource: pods
matchScopes:
- scopeName: PriorityClass
operator: In
values:
- cluster-services
`,
expectConfig: &resourcequotaapi.Configuration{
LimitedResources: []resourcequotaapi.LimitedResource{
{APIGroup: "", Resource: "persistentvolumeclaims", MatchContains: []string{".storageclass.storage.k8s.io/requests.storage"}},
{APIGroup: "", Resource: "pods", MatchScopes: []corev1.ScopedResourceSelectorRequirement{
{ScopeName: "PriorityClass", Operator: "In", Values: []string{"cluster-services"}},
},
},
}},
},
{
name: "valid v1 config",
input: `
kind: ResourceQuotaConfiguration
apiVersion: apiserver.config.k8s.io/v1
limitedResources:
- apiGroup: ""
resource: persistentvolumeclaims
matchContains:
- .storageclass.storage.k8s.io/requests.storage
- apiGroup: ""
resource: pods
matchScopes:
- scopeName: PriorityClass
operator: In
values:
- cluster-services
`,
expectConfig: &resourcequotaapi.Configuration{
LimitedResources: []resourcequotaapi.LimitedResource{
{APIGroup: "", Resource: "persistentvolumeclaims", MatchContains: []string{".storageclass.storage.k8s.io/requests.storage"}},
{APIGroup: "", Resource: "pods", MatchScopes: []corev1.ScopedResourceSelectorRequirement{
{ScopeName: "PriorityClass", Operator: "In", Values: []string{"cluster-services"}},
},
},
}},
},
}
for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
config, err := LoadConfiguration(bytes.NewBuffer([]byte(tc.input)))
if len(tc.expectErr) > 0 {
if err == nil {
t.Fatal("expected err, got none")
}
if !strings.Contains(err.Error(), tc.expectErr) {
t.Fatalf("expected err containing %q, got %v", tc.expectErr, err)
}
return
}
if err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(config, tc.expectConfig) {
t.Fatalf("expected\n%#v\ngot\n%#v", tc.expectConfig, config)
}
})
}
}