mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 05:57:25 +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