mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 04:06:03 +00:00
Merge pull request #49891 from p0lyn0mial/sample_server_admission_plugin
Automatic merge from submit-queue (batch tested with PRs 49990, 49997, 44278, 49936, 49891) adds an admission plugin to the sample apiserver. **What this PR does / why we need it**: adds an admission plugin to the sample apiserver. the admission plugin checks whether `Flunder.Name` is not on the banned list. including a unit test with various test scenarios. **Special notes for your reviewer**: https://github.com/kubernetes/kubernetes/issues/47868 **Release note**: ``` NONE ```
This commit is contained in:
commit
64a984bb62
@ -0,0 +1,42 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["admission.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/admission/wardleinitializer:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/apis/wardle:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/client/informers_generated/internalversion:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/client/listers_generated/wardle/internalversion:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_xtest",
|
||||
srcs = ["admission_test.go"],
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/admission:go_default_library",
|
||||
"//vendor/k8s.io/client-go/testing:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/admission/plugin/banflunder:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/admission/wardleinitializer:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/apis/wardle:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/client/clientset_generated/internalclientset/fake:go_default_library",
|
||||
"//vendor/k8s.io/sample-apiserver/pkg/client/informers_generated/internalversion:go_default_library",
|
||||
],
|
||||
)
|
@ -0,0 +1,84 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package banflunder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
|
||||
"k8s.io/sample-apiserver/pkg/apis/wardle"
|
||||
informers "k8s.io/sample-apiserver/pkg/client/informers_generated/internalversion"
|
||||
listers "k8s.io/sample-apiserver/pkg/client/listers_generated/wardle/internalversion"
|
||||
)
|
||||
|
||||
type disallowFlunder struct {
|
||||
*admission.Handler
|
||||
lister listers.FischerLister
|
||||
}
|
||||
|
||||
var _ = wardleinitializer.WantsInternalWardleInformerFactory(&disallowFlunder{})
|
||||
|
||||
// Admit ensures that the object in-flight is of kind Flunder.
|
||||
// In addition checks that the Name is not on the banned list.
|
||||
// The list is stored in Fischers API objects.
|
||||
func (d *disallowFlunder) Admit(a admission.Attributes) error {
|
||||
// we are only interested in flunders
|
||||
if a.GetKind().GroupKind() != wardle.Kind("Flunder") {
|
||||
return nil
|
||||
}
|
||||
|
||||
flunderName := a.GetName()
|
||||
fischers, err := d.lister.List(labels.Everything())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, fischer := range fischers {
|
||||
for _, disallowedFlunder := range fischer.DisallowedFlunders {
|
||||
if flunderName == disallowedFlunder {
|
||||
return errors.NewForbidden(
|
||||
a.GetResource().GroupResource(),
|
||||
a.GetName(),
|
||||
fmt.Errorf("this name may not be used, please change the resource name"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetInternalWardleInformerFactory gets Lister from SharedInformerFactory.
|
||||
// The lister knows how to lists Fischers.
|
||||
func (d *disallowFlunder) SetInternalWardleInformerFactory(f informers.SharedInformerFactory) {
|
||||
d.lister = f.Wardle().InternalVersion().Fischers().Lister()
|
||||
}
|
||||
|
||||
// Validate checks whether the plugin was correctly initialized.
|
||||
func (d *disallowFlunder) Validate() error {
|
||||
if d.lister == nil {
|
||||
return fmt.Errorf("missing fischer lister")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// New creates a new ban flunder admission plugin
|
||||
func New() (admission.Interface, error) {
|
||||
return &disallowFlunder{}, nil
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
Copyright 2017 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package banflunder_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
clienttesting "k8s.io/client-go/testing"
|
||||
"k8s.io/sample-apiserver/pkg/admission/plugin/banflunder"
|
||||
"k8s.io/sample-apiserver/pkg/admission/wardleinitializer"
|
||||
"k8s.io/sample-apiserver/pkg/apis/wardle"
|
||||
"k8s.io/sample-apiserver/pkg/client/clientset_generated/internalclientset/fake"
|
||||
informers "k8s.io/sample-apiserver/pkg/client/informers_generated/internalversion"
|
||||
)
|
||||
|
||||
// TestBanfluderAdmissionPlugin tests various test cases against
|
||||
// ban flunder admission plugin
|
||||
func TestBanflunderAdmissionPlugin(t *testing.T) {
|
||||
var scenarios = []struct {
|
||||
informersOutput wardle.FischerList
|
||||
admissionInput wardle.Flunder
|
||||
admissionInputKind schema.GroupVersionKind
|
||||
admissionInputResource schema.GroupVersionResource
|
||||
admissionMustFail bool
|
||||
}{
|
||||
// scenario 1:
|
||||
// a flunder with a name that appears on a list of disallowed flunders must be banned
|
||||
{
|
||||
informersOutput: wardle.FischerList{
|
||||
Items: []wardle.Fischer{
|
||||
{DisallowedFlunders: []string{"badname"}},
|
||||
},
|
||||
},
|
||||
admissionInput: wardle.Flunder{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "badname",
|
||||
Namespace: "",
|
||||
},
|
||||
},
|
||||
admissionInputKind: wardle.Kind("Flunder").WithVersion("version"),
|
||||
admissionInputResource: wardle.Resource("flunders").WithVersion("version"),
|
||||
admissionMustFail: true,
|
||||
},
|
||||
// scenario 2:
|
||||
// a flunder with a name that does not appear on a list of disallowed flunders must be admitted
|
||||
{
|
||||
informersOutput: wardle.FischerList{
|
||||
Items: []wardle.Fischer{
|
||||
{DisallowedFlunders: []string{"badname"}},
|
||||
},
|
||||
},
|
||||
admissionInput: wardle.Flunder{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "goodname",
|
||||
Namespace: "",
|
||||
},
|
||||
},
|
||||
admissionInputKind: wardle.Kind("Flunder").WithVersion("version"),
|
||||
admissionInputResource: wardle.Resource("flunders").WithVersion("version"),
|
||||
admissionMustFail: false,
|
||||
},
|
||||
// scenario 3:
|
||||
// a flunder with a name that appears on a list of disallowed flunders would be banned
|
||||
// but the kind passed in is not a flunder thus the whole request is accepted
|
||||
{
|
||||
informersOutput: wardle.FischerList{
|
||||
Items: []wardle.Fischer{
|
||||
{DisallowedFlunders: []string{"badname"}},
|
||||
},
|
||||
},
|
||||
admissionInput: wardle.Flunder{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "badname",
|
||||
Namespace: "",
|
||||
},
|
||||
},
|
||||
admissionInputKind: wardle.Kind("NotFlunder").WithVersion("version"),
|
||||
admissionInputResource: wardle.Resource("notflunders").WithVersion("version"),
|
||||
admissionMustFail: false,
|
||||
},
|
||||
}
|
||||
|
||||
for index, scenario := range scenarios {
|
||||
func() {
|
||||
// prepare
|
||||
cs := &fake.Clientset{}
|
||||
cs.AddReactor("list", "fischers", func(action clienttesting.Action) (bool, runtime.Object, error) {
|
||||
return true, &scenario.informersOutput, nil
|
||||
})
|
||||
informersFactory := informers.NewSharedInformerFactory(cs, 5*time.Minute)
|
||||
|
||||
target, err := banflunder.New()
|
||||
if err != nil {
|
||||
t.Fatalf("scenario %d: failed to create banflunder admission plugin due to = %v", index, err)
|
||||
}
|
||||
|
||||
targetInitializer, err := wardleinitializer.New(informersFactory)
|
||||
if err != nil {
|
||||
t.Fatalf("scenario %d: failed to crate wardle plugin initializer due to = %v", index, err)
|
||||
}
|
||||
targetInitializer.Initialize(target)
|
||||
|
||||
err = admission.Validate(target)
|
||||
if err != nil {
|
||||
t.Fatalf("scenario %d: failed to initialize banflunder admission plugin due to =%v", index, err)
|
||||
}
|
||||
|
||||
stop := make(chan struct{})
|
||||
defer close(stop)
|
||||
informersFactory.Start(stop)
|
||||
informersFactory.WaitForCacheSync(stop)
|
||||
|
||||
// act
|
||||
err = target.Admit(admission.NewAttributesRecord(
|
||||
&scenario.admissionInput,
|
||||
nil,
|
||||
scenario.admissionInputKind,
|
||||
scenario.admissionInput.ObjectMeta.Namespace,
|
||||
scenario.admissionInput.ObjectMeta.Name,
|
||||
scenario.admissionInputResource,
|
||||
"",
|
||||
admission.Create,
|
||||
nil),
|
||||
)
|
||||
|
||||
// validate
|
||||
if scenario.admissionMustFail && err == nil {
|
||||
t.Errorf("scenario %d: expected an error but got nothing", index)
|
||||
}
|
||||
|
||||
if !scenario.admissionMustFail && err != nil {
|
||||
t.Errorf("scenario %d: banflunder admission plugin returned unexpected error = %v", index, err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user