Unverified Commit e4c35406 authored by Kubernetes Prow Robot's avatar Kubernetes Prow Robot Committed by GitHub

Merge pull request #78269 from jpbetz/reinvoke-intree

Reinvocation testing of in-tree admission plugins and fix podpreset to be idempotent
parents 51336deb cc2e3616
......@@ -25,6 +25,10 @@ type key struct {
effect api.TaintEffect
}
func convertTolerationToKey(in api.Toleration) key {
return key{in.Key, in.Effect}
}
// VerifyAgainstWhitelist checks if the provided tolerations
// satisfy the provided whitelist and returns true, otherwise returns false
func VerifyAgainstWhitelist(tolerations []api.Toleration, whitelist []api.Toleration) bool {
......@@ -62,13 +66,14 @@ func IsConflict(first []api.Toleration, second []api.Toleration) bool {
func MergeTolerations(first []api.Toleration, second []api.Toleration) []api.Toleration {
var mergedTolerations []api.Toleration
mergedTolerations = append(mergedTolerations, second...)
firstMap := ConvertTolerationToAMap(first)
secondMap := ConvertTolerationToAMap(second)
for k1, v1 := range firstMap {
if _, ok := secondMap[k1]; !ok {
mergedTolerations = append(mergedTolerations, v1)
// preserve order of first when merging
for _, v := range first {
k := convertTolerationToKey(v)
// if first contains key conflicts, the last one takes precedence
if _, ok := secondMap[k]; !ok && firstMap[k] == v {
mergedTolerations = append(mergedTolerations, v)
}
}
return mergedTolerations
......@@ -95,8 +100,8 @@ func EqualTolerations(first []api.Toleration, second []api.Toleration) bool {
// ConvertTolerationToAMap converts toleration list into a map[string]api.Toleration
func ConvertTolerationToAMap(in []api.Toleration) map[key]api.Toleration {
out := map[key]api.Toleration{}
for i := range in {
out[key{in[i].Key, in[i].Effect}] = in[i]
for _, v := range in {
out[convertTolerationToKey(v)] = v
}
return out
}
......
......@@ -24,6 +24,7 @@ go_test(
"//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
],
)
......
......@@ -21,12 +21,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestAdmissionNonNilAttribute(t *testing.T) {
handler := NewAlwaysAdmit()
err := handler.(*alwaysAdmit).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
handler := admissiontesting.WithReinvocationTesting(t, NewAlwaysAdmit().(*alwaysAdmit))
err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil {
t.Errorf("Unexpected error returned from admission handler")
}
......
......@@ -27,6 +27,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
],
)
......
......@@ -22,6 +22,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
api "k8s.io/kubernetes/pkg/apis/core"
)
......@@ -29,7 +30,7 @@ import (
// set to Always
func TestAdmission(t *testing.T) {
namespace := "test"
handler := &AlwaysPullImages{}
handler := admissiontesting.WithReinvocationTesting(t, &AlwaysPullImages{})
pod := api.Pod{
ObjectMeta: metav1.ObjectMeta{Name: "123", Namespace: namespace},
Spec: api.PodSpec{
......@@ -137,7 +138,7 @@ func TestOtherResources(t *testing.T) {
}
for _, tc := range tests {
handler := &AlwaysPullImages{}
handler := admissiontesting.WithReinvocationTesting(t, &AlwaysPullImages{})
err := handler.Admit(admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, &metav1.CreateOptions{}, false, nil), nil)
......
......@@ -15,6 +15,7 @@ go_test(
"//pkg/apis/core/helper:go_default_library",
"//pkg/scheduler/api:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
],
)
......
......@@ -20,6 +20,7 @@ import (
"testing"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
......@@ -32,7 +33,7 @@ func TestForgivenessAdmission(t *testing.T) {
return &s
}
handler := NewDefaultTolerationSeconds()
handler := admissiontesting.WithReinvocationTesting(t, NewDefaultTolerationSeconds())
// NOTE: for anyone who want to modify this test, the order of tolerations matters!
tests := []struct {
description string
......
......@@ -24,6 +24,7 @@ go_test(
"//pkg/apis/core:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
],
)
......
......@@ -21,12 +21,13 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
api "k8s.io/kubernetes/pkg/apis/core"
)
func TestAdmission(t *testing.T) {
handler := NewAlwaysDeny()
err := handler.(*alwaysDeny).Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
handler := admissiontesting.WithReinvocationTesting(t, NewAlwaysDeny().(*alwaysDeny))
err := handler.Admit(admission.NewAttributesRecord(nil, nil, api.Kind("kind").WithVersion("version"), "namespace", "name", api.Resource("resource").WithVersion("version"), "subresource", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err == nil {
t.Error("Expected error returned from admission handler")
}
......
......@@ -23,6 +23,7 @@ go_test(
"//pkg/apis/core/helper:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
],
)
......
......@@ -21,13 +21,14 @@ import (
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
"k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/core/helper"
)
func TestAdmit(t *testing.T) {
plugin := newExtendedResourceToleration()
plugin := admissiontesting.WithReinvocationTesting(t, newExtendedResourceToleration())
containerRequestingCPU := core.Container{
Resources: core.ResourceRequirements{
......
......@@ -46,6 +46,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
......
......@@ -30,6 +30,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
......@@ -705,7 +706,7 @@ func TestLimitRangerIgnoresSubresource(t *testing.T) {
informerFactory.Start(wait.NeverStop)
testPod := validPod("testPod", 1, api.ResourceRequirements{})
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil {
t.Fatal(err)
}
......@@ -735,7 +736,7 @@ func TestLimitRangerAdmitPod(t *testing.T) {
informerFactory.Start(wait.NeverStop)
testPod := validPod("testPod", 1, api.ResourceRequirements{})
err = handler.Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&testPod, nil, api.Kind("Pod").WithVersion("version"), limitRange.Namespace, "testPod", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil {
t.Fatal(err)
}
......
......@@ -36,6 +36,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
......
......@@ -28,6 +28,7 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
"k8s.io/client-go/informers"
clientset "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/fake"
......@@ -99,7 +100,7 @@ func TestAdmission(t *testing.T) {
informerFactory.Start(wait.NeverStop)
pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil {
t.Errorf("unexpected error returned from admission handler")
}
......@@ -119,7 +120,7 @@ func TestAdmissionNamespaceExists(t *testing.T) {
informerFactory.Start(wait.NeverStop)
pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil {
t.Errorf("unexpected error returned from admission handler")
}
......@@ -139,7 +140,7 @@ func TestAdmissionDryRun(t *testing.T) {
informerFactory.Start(wait.NeverStop)
pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, true, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, true, nil), nil)
if err != nil {
t.Errorf("unexpected error returned from admission handler")
}
......@@ -157,10 +158,10 @@ func TestIgnoreAdmission(t *testing.T) {
t.Errorf("unexpected error initializing handler: %v", err)
}
informerFactory.Start(wait.NeverStop)
chainHandler := admission.NewChainHandler(handler)
chainHandler := admissiontesting.WithReinvocationTesting(t, admission.NewChainHandler(handler))
pod := newPod(namespace)
err = chainHandler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, chainHandler).Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil), nil)
if err != nil {
t.Errorf("unexpected error returned from admission handler")
}
......@@ -182,7 +183,7 @@ func TestAdmissionWithLatentCache(t *testing.T) {
informerFactory.Start(wait.NeverStop)
pod := newPod(namespace)
err = handler.Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), pod.Namespace, pod.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if err != nil {
t.Errorf("unexpected error returned from admission handler")
}
......
......@@ -17,11 +17,14 @@ go_test(
"//staging/src/k8s.io/api/settings/v1alpha1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/listers/settings/v1alpha1:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
],
)
......
......@@ -252,21 +252,56 @@ func mergeEnv(envVars []api.EnvVar, podPresets []*settingsv1alpha1.PodPreset) ([
return mergedEnv, err
}
type envFromMergeKey struct {
prefix string
configMapRefName string
secretRefName string
}
func newEnvFromMergeKey(e api.EnvFromSource) envFromMergeKey {
k := envFromMergeKey{prefix: e.Prefix}
if e.ConfigMapRef != nil {
k.configMapRefName = e.ConfigMapRef.Name
}
if e.SecretRef != nil {
k.secretRefName = e.SecretRef.Name
}
return k
}
func mergeEnvFrom(envSources []api.EnvFromSource, podPresets []*settingsv1alpha1.PodPreset) ([]api.EnvFromSource, error) {
var mergedEnvFrom []api.EnvFromSource
// merge envFrom using a identify key to ensure Admit reinvocations are idempotent
origEnvSources := map[envFromMergeKey]api.EnvFromSource{}
for _, envSource := range envSources {
origEnvSources[newEnvFromMergeKey(envSource)] = envSource
}
mergedEnvFrom = append(mergedEnvFrom, envSources...)
var errs []error
for _, pp := range podPresets {
for _, envFromSource := range pp.Spec.EnvFrom {
internalEnvFrom := api.EnvFromSource{}
if err := apiscorev1.Convert_v1_EnvFromSource_To_core_EnvFromSource(&envFromSource, &internalEnvFrom, nil); err != nil {
return nil, err
}
mergedEnvFrom = append(mergedEnvFrom, internalEnvFrom)
found, ok := origEnvSources[newEnvFromMergeKey(internalEnvFrom)]
if !ok {
mergedEnvFrom = append(mergedEnvFrom, internalEnvFrom)
continue
}
if !reflect.DeepEqual(found, internalEnvFrom) {
errs = append(errs, fmt.Errorf("merging envFrom for %s has a conflict: \n%#v\ndoes not match\n%#v\n in container", pp.GetName(), internalEnvFrom, found))
}
}
}
err := utilerrors.NewAggregate(errs)
if err != nil {
return nil, err
}
return mergedEnvFrom, nil
}
......
......@@ -17,14 +17,18 @@ limitations under the License.
package podpreset
import (
"fmt"
"reflect"
"testing"
fuzz "github.com/google/gofuzz"
corev1 "k8s.io/api/core/v1"
settingsv1alpha1 "k8s.io/api/settings/v1alpha1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
kadmission "k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes/fake"
......@@ -444,7 +448,7 @@ func TestAdmitConflictWithDifferentNamespaceShouldDoNothing(t *testing.T) {
},
}
err := admitPod(pod, pip)
err := admitPod(t, pod, pip)
if err != nil {
t.Fatal(err)
}
......@@ -490,7 +494,7 @@ func TestAdmitConflictWithNonMatchingLabelsShouldNotError(t *testing.T) {
},
}
err := admitPod(pod, pip)
err := admitPod(t, pod, pip)
if err != nil {
t.Fatal(err)
}
......@@ -537,7 +541,7 @@ func TestAdmitConflictShouldNotModifyPod(t *testing.T) {
},
}
err := admitPod(pod, pip)
err := admitPod(t, pod, pip)
if err != nil {
t.Fatal(err)
}
......@@ -600,7 +604,7 @@ func TestAdmit(t *testing.T) {
},
}
err := admitPod(pod, pip)
err := admitPod(t, pod, pip)
if err != nil {
t.Fatal(err)
}
......@@ -660,7 +664,7 @@ func TestAdmitMirrorPod(t *testing.T) {
},
}
if err := admitPod(mirrorPod, pip); err != nil {
if err := admitPod(t, mirrorPod, pip); err != nil {
t.Fatal(err)
}
......@@ -730,7 +734,7 @@ func TestExclusionNoAdmit(t *testing.T) {
},
}
originalPod := pod.DeepCopy()
err := admitPod(pod, pip)
err := admitPod(t, pod, pip)
if err != nil {
t.Fatal(err)
}
......@@ -794,7 +798,7 @@ func TestAdmitEmptyPodNamespace(t *testing.T) {
},
}
originalPod := pod.DeepCopy()
err := admitPod(pod, pip)
err := admitPod(t, pod, pip)
if err != nil {
t.Fatal(err)
}
......@@ -805,11 +809,11 @@ func TestAdmitEmptyPodNamespace(t *testing.T) {
}
}
func admitPod(pod *api.Pod, pip *settingsv1alpha1.PodPreset) error {
func admitPod(t *testing.T, pod *api.Pod, pip *settingsv1alpha1.PodPreset) error {
informerFactory := informers.NewSharedInformerFactory(nil, controller.NoResyncPeriodFunc())
store := informerFactory.Settings().V1alpha1().PodPresets().Informer().GetStore()
store.Add(pip)
plugin := NewTestAdmission(informerFactory.Settings().V1alpha1().PodPresets().Lister())
plugin := admissiontesting.WithReinvocationTesting(t, NewTestAdmission(informerFactory.Settings().V1alpha1().PodPresets().Lister()))
attrs := kadmission.NewAttributesRecord(
pod,
nil,
......@@ -831,3 +835,55 @@ func admitPod(pod *api.Pod, pip *settingsv1alpha1.PodPreset) error {
return nil
}
func TestEnvFromMergeKey(t *testing.T) {
f := fuzz.New()
for i := 0; i < 100; i++ {
t.Run(fmt.Sprintf("Run %d/100", i), func(t *testing.T) {
orig := api.EnvFromSource{}
f.Fuzz(&orig)
clone := api.EnvFromSource{}
f.Fuzz(&clone)
key := newEnvFromMergeKey(orig)
// copy all key fields into the clone so it only differs by fields not from the key
clone.Prefix = key.prefix
if orig.ConfigMapRef == nil {
clone.ConfigMapRef = nil
} else {
if clone.ConfigMapRef == nil {
clone.ConfigMapRef = &api.ConfigMapEnvSource{
LocalObjectReference: api.LocalObjectReference{},
}
}
clone.ConfigMapRef.Name = key.configMapRefName
}
if orig.SecretRef == nil {
clone.SecretRef = nil
} else {
if clone.SecretRef == nil {
clone.SecretRef = &api.SecretEnvSource{
LocalObjectReference: api.LocalObjectReference{},
}
}
clone.SecretRef.Name = key.secretRefName
}
// zero out known non-identifying fields
for _, e := range []api.EnvFromSource{orig, clone} {
if e.ConfigMapRef != nil {
e.ConfigMapRef.Optional = nil
}
if e.SecretRef != nil {
e.SecretRef.Optional = nil
}
}
if !reflect.DeepEqual(orig, clone) {
t.Errorf("expected all but known non-identifying fields for envFrom to be in envFromMergeKey but found unaccounted for differences, diff:\n%s", diff.ObjectReflectDiff(orig, clone))
}
})
}
}
......@@ -21,6 +21,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/initializer:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
......
......@@ -26,6 +26,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
"k8s.io/client-go/kubernetes"
......@@ -265,8 +266,7 @@ func TestPodAdmission(t *testing.T) {
handler.pluginConfig = &pluginapi.Configuration{Default: test.defaultClusterTolerations, Whitelist: test.clusterWhitelist}
pod := test.pod
pod.Spec.Tolerations = test.podTolerations
err = handler.Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if test.admit && err != nil {
t.Errorf("Test: %s, expected no error but got: %s", test.testName, err)
} else if !test.admit && err == nil {
......@@ -343,7 +343,7 @@ func TestIgnoreUpdatingInitializedPod(t *testing.T) {
}
// if the update of initialized pod is not ignored, an error will be returned because the pod's Tolerations conflicts with namespace's Tolerations.
err = handler.Admit(admission.NewAttributesRecord(pod, pod, api.Kind("Pod").WithVersion("version"), "testNamespace", pod.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil), nil)
err = admissiontesting.WithReinvocationTesting(t, handler).Admit(admission.NewAttributesRecord(pod, pod, api.Kind("Pod").WithVersion("version"), "testNamespace", pod.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.CreateOptions{}, false, nil), nil)
if err != nil {
t.Errorf("expected no error, got: %v", err)
}
......
......@@ -19,6 +19,7 @@ go_test(
"//staging/src/k8s.io/api/scheduling/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
......
......@@ -24,6 +24,7 @@ import (
schedulingv1 "k8s.io/api/scheduling/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
"k8s.io/apiserver/pkg/authentication/user"
utilfeature "k8s.io/apiserver/pkg/util/feature"
"k8s.io/client-go/informers"
......@@ -605,7 +606,7 @@ func TestPodAdmission(t *testing.T) {
false,
nil,
)
err := ctrl.Admit(attrs, nil)
err := admissiontesting.WithReinvocationTesting(t, ctrl).Admit(attrs, nil)
klog.Infof("Got %v", err)
if !test.expectError {
if err != nil {
......
......@@ -51,6 +51,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
......
......@@ -30,6 +30,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
kadmission "k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
"k8s.io/apiserver/pkg/authentication/serviceaccount"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/apiserver/pkg/authorization/authorizer"
......@@ -1779,7 +1780,7 @@ func testPSPAdmitAdvanced(testCaseName string, op kadmission.Operation, psps []*
attrs := kadmission.NewAttributesRecord(pod, oldPod, kapi.Kind("Pod").WithVersion("version"), pod.Namespace, "", kapi.Resource("pods").WithVersion("version"), "", op, nil, false, userInfo)
annotations := make(map[string]string)
attrs = &fakeAttributes{attrs, annotations}
err := plugin.Admit(attrs, nil)
err := admissiontesting.WithReinvocationTesting(t, plugin).Admit(attrs, nil)
if shouldPassAdmit && err != nil {
t.Errorf("%s: expected no errors on Admit but received %v", testCaseName, err)
......
......@@ -49,6 +49,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
......
......@@ -37,6 +37,7 @@ go_test(
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/cloud-provider:go_default_library",
],
)
......
......@@ -28,6 +28,7 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
cloudprovider "k8s.io/cloud-provider"
api "k8s.io/kubernetes/pkg/apis/core"
)
......@@ -754,7 +755,7 @@ func Test_PVLAdmission(t *testing.T) {
for _, testcase := range testcases {
t.Run(testcase.name, func(t *testing.T) {
setPVLabeler(testcase.handler, testcase.pvlabeler)
handler := admission.NewChainHandler(testcase.handler)
handler := admissiontesting.WithReinvocationTesting(t, admission.NewChainHandler(testcase.handler))
err := handler.Admit(admission.NewAttributesRecord(testcase.preAdmissionPV, nil, api.Kind("PersistentVolume").WithVersion("version"), testcase.preAdmissionPV.Namespace, testcase.preAdmissionPV.Name, api.Resource("persistentvolumes").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
if !reflect.DeepEqual(err, testcase.err) {
......
......@@ -36,6 +36,7 @@ go_test(
"//staging/src/k8s.io/api/storage/v1:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:go_default_library",
"//staging/src/k8s.io/client-go/informers:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
......
......@@ -24,6 +24,7 @@ import (
storagev1 "k8s.io/api/storage/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apiserver/pkg/admission"
admissiontesting "k8s.io/apiserver/pkg/admission/testing"
"k8s.io/client-go/informers"
api "k8s.io/kubernetes/pkg/apis/core"
storageutil "k8s.io/kubernetes/pkg/apis/storage/util"
......@@ -212,7 +213,7 @@ func TestAdmission(t *testing.T) {
false, // dryRun
nil, // userInfo
)
err := ctrl.Admit(attrs, nil)
err := admissiontesting.WithReinvocationTesting(t, ctrl).Admit(attrs, nil)
klog.Infof("Got %v", err)
if err != nil && !test.expectError {
t.Errorf("Test %q: unexpected error received: %v", test.name, err)
......
......@@ -92,6 +92,7 @@ filegroup(
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/testing:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/util:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating:all-srcs",
"//staging/src/k8s.io/apiserver/pkg/admission/testing:all-srcs",
],
tags = ["automanaged"],
)
load("@io_bazel_rules_go//go:def.bzl", "go_library")
go_library(
name = "go_default_library",
srcs = ["helpers.go"],
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/admission/testing",
importpath = "k8s.io/apiserver/pkg/admission/testing",
visibility = ["//visibility:public"],
deps = [
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)
/*
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 testing
import (
"reflect"
"testing"
apiequality "k8s.io/apimachinery/pkg/api/equality"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apiserver/pkg/admission"
)
// WithReinvocationTesting wraps a mutating admission handler and reinvokes it each time Admit is
// called. It checks the admission output object and reports a test error if the admission handler
// performs non-idempotent mutatations to the object.
func WithReinvocationTesting(t *testing.T, admission admission.MutationInterface) admission.MutationInterface {
return &reinvoker{t, admission}
}
type reinvoker struct {
t *testing.T
admission admission.MutationInterface
}
// Admit reinvokes the admission handler and reports a test error if the admission handler performs
// non-idempotent mutatations to the admission object.
func (r *reinvoker) Admit(a admission.Attributes, o admission.ObjectInterfaces) error {
r.t.Helper()
outputs := []runtime.Object{}
for i := 0; i < 2; i++ {
err := r.admission.Admit(a, o)
if err != nil {
return err
}
if a.GetObject() != nil {
// keep a copy of the output for subsequent idempotency checking
outputs = append(outputs, a.GetObject().DeepCopyObject())
// replace a.GetObject() with a copy of itself to make sure admission is safe to reinvoke with a round-tripped copy (no pointer comparisons are done)
if deepCopyInto, ok := reflect.TypeOf(a.GetObject()).MethodByName("DeepCopyInto"); ok {
deepCopyInto.Func.Call([]reflect.Value{
reflect.ValueOf(a.GetObject().DeepCopyObject()),
reflect.ValueOf(a.GetObject()),
})
}
}
}
for i := 1; i < len(outputs); i++ {
if !apiequality.Semantic.DeepEqual(outputs[0], outputs[i]) {
r.t.Errorf("expected mutating admission plugin to be idempontent, but got different results on reinvocation, diff:\n%s", diff.ObjectReflectDiff(outputs[0], outputs[i]))
}
}
return nil
}
// Handles will return true if any of the admission andler handlers handle the given operation.
func (r *reinvoker) Handles(operation admission.Operation) bool {
r.t.Helper()
return r.admission.Handles(operation)
}
......@@ -1195,6 +1195,7 @@ k8s.io/apiserver/pkg/admission/plugin/webhook/request
k8s.io/apiserver/pkg/admission/plugin/webhook/rules
k8s.io/apiserver/pkg/admission/plugin/webhook/util
k8s.io/apiserver/pkg/admission/plugin/webhook/validating
k8s.io/apiserver/pkg/admission/testing
k8s.io/apiserver/pkg/apis/apiserver
k8s.io/apiserver/pkg/apis/apiserver/install
k8s.io/apiserver/pkg/apis/apiserver/v1alpha1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment