diff --git a/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage/storage_test.go b/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage/storage_test.go new file mode 100644 index 00000000000..cc16752bf3f --- /dev/null +++ b/pkg/registry/admissionregistration/validatingwebhookconfiguration/storage/storage_test.go @@ -0,0 +1,201 @@ +/* +Copyright 2021 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 storage + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apiserver/pkg/registry/generic" + genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing" + etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" + "k8s.io/kubernetes/pkg/apis/admissionregistration" + "k8s.io/kubernetes/pkg/registry/registrytest" + + // Ensure that admissionregistration package is initialized. + _ "k8s.io/kubernetes/pkg/apis/admissionregistration/install" +) + +func TestCreate(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := genericregistrytest.New(t, storage.Store).ClusterScope() + configuration := validValidatingWebhookConfiguration() + test.TestCreate( + // valid + configuration, + // invalid + newValidatingWebhookConfiguration(""), + ) +} + +func TestUpdate(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := genericregistrytest.New(t, storage.Store).ClusterScope() + + test.TestUpdate( + // valid + validValidatingWebhookConfiguration(), + // updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*admissionregistration.ValidatingWebhookConfiguration) + object.Labels = map[string]string{"c": "d"} + return object + }, + // invalid updateFunc + func(obj runtime.Object) runtime.Object { + object := obj.(*admissionregistration.ValidatingWebhookConfiguration) + object.Name = "" + return object + }, + ) +} + +func TestGet(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := genericregistrytest.New(t, storage.Store).ClusterScope() + test.TestGet(validValidatingWebhookConfiguration()) +} + +func TestList(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := genericregistrytest.New(t, storage.Store).ClusterScope() + test.TestList(validValidatingWebhookConfiguration()) +} + +func TestDelete(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := genericregistrytest.New(t, storage.Store).ClusterScope() + test.TestDelete(validValidatingWebhookConfiguration()) +} + +func TestWatch(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + test := genericregistrytest.New(t, storage.Store).ClusterScope() + test.TestWatch( + validValidatingWebhookConfiguration(), + []labels.Set{}, + []labels.Set{ + {"hoo": "bar"}, + }, + []fields.Set{ + {"metadata.name": "foo"}, + }, + []fields.Set{ + {"metadata.name": "nomatch"}, + }, + ) +} + +func validValidatingWebhookConfiguration() *admissionregistration.ValidatingWebhookConfiguration { + ignore := admissionregistration.Ignore + exact := admissionregistration.Exact + thirty := int32(30) + none := admissionregistration.SideEffectClassNone + servicePath := "/" + return &admissionregistration.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Webhooks: []admissionregistration.ValidatingWebhook{{ + Name: "foo.example.io", + ClientConfig: admissionregistration.WebhookClientConfig{ + Service: &admissionregistration.ServiceReference{ + Name: "foo", + Namespace: "bar", + Path: &servicePath, + Port: 443, + }, + }, + FailurePolicy: &ignore, + MatchPolicy: &exact, + TimeoutSeconds: &thirty, + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, + SideEffects: &none, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + } +} + +func newValidatingWebhookConfiguration(name string) *admissionregistration.ValidatingWebhookConfiguration { + ignore := admissionregistration.Ignore + exact := admissionregistration.Exact + thirty := int32(30) + none := admissionregistration.SideEffectClassNone + servicePath := "/" + return &admissionregistration.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: map[string]string{"foo": "bar"}, + }, + Webhooks: []admissionregistration.ValidatingWebhook{{ + Name: "foo.example.io", + ClientConfig: admissionregistration.WebhookClientConfig{ + Service: &admissionregistration.ServiceReference{ + Name: "foo", + Namespace: "bar", + Path: &servicePath, + Port: 443, + }, + }, + FailurePolicy: &ignore, + MatchPolicy: &exact, + TimeoutSeconds: &thirty, + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, + SideEffects: &none, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + } +} + +func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) { + etcdStorage, server := registrytest.NewEtcdStorage(t, admissionregistration.GroupName) + restOptions := generic.RESTOptions{ + StorageConfig: etcdStorage, + Decorator: generic.UndecoratedStorage, + DeleteCollectionWorkers: 1, + ResourcePrefix: "validatingwebhookconfigurations"} + storage, err := NewREST(restOptions) + if err != nil { + t.Fatalf("unexpected error from REST storage: %v", err) + } + return storage, server +} + +func TestCategories(t *testing.T) { + storage, server := newStorage(t) + defer server.Terminate(t) + defer storage.Store.DestroyFunc() + expected := []string{"api-extensions"} + registrytest.AssertCategories(t, storage, expected) +} diff --git a/pkg/registry/admissionregistration/validatingwebhookconfiguration/strategy_test.go b/pkg/registry/admissionregistration/validatingwebhookconfiguration/strategy_test.go new file mode 100644 index 00000000000..0d5408d2892 --- /dev/null +++ b/pkg/registry/admissionregistration/validatingwebhookconfiguration/strategy_test.go @@ -0,0 +1,80 @@ +/* +Copyright 2021 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 validatingwebhookconfiguration + +import ( + "testing" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + "k8s.io/kubernetes/pkg/apis/admissionregistration" +) + +func TestValidatingWebhookConfigurationStrategy(t *testing.T) { + ctx := genericapirequest.NewDefaultContext() + if Strategy.NamespaceScoped() { + t.Error("ValidatingWebhookConfiguration strategy must be cluster scoped") + } + if Strategy.AllowCreateOnUpdate() { + t.Errorf("ValidatingWebhookConfiguration should not allow create on update") + } + + configuration := validValidatingWebhookConfiguration() + Strategy.PrepareForCreate(ctx, configuration) + errs := Strategy.Validate(ctx, configuration) + if len(errs) != 0 { + t.Errorf("Unexpected error validating %v", errs) + } + invalidConfiguration := &admissionregistration.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{Name: ""}, + } + Strategy.PrepareForUpdate(ctx, invalidConfiguration, configuration) + errs = Strategy.ValidateUpdate(ctx, invalidConfiguration, configuration) + if len(errs) == 0 { + t.Errorf("Expected a validation error") + } +} +func validValidatingWebhookConfiguration() *admissionregistration.ValidatingWebhookConfiguration { + ignore := admissionregistration.Ignore + exact := admissionregistration.Exact + thirty := int32(30) + none := admissionregistration.SideEffectClassNone + servicePath := "/" + return &admissionregistration.ValidatingWebhookConfiguration{ + ObjectMeta: metav1.ObjectMeta{ + Name: "foo", + }, + Webhooks: []admissionregistration.ValidatingWebhook{{ + Name: "foo.example.io", + ClientConfig: admissionregistration.WebhookClientConfig{ + Service: &admissionregistration.ServiceReference{ + Name: "foo", + Namespace: "bar", + Path: &servicePath, + Port: 443, + }, + }, + FailurePolicy: &ignore, + MatchPolicy: &exact, + TimeoutSeconds: &thirty, + NamespaceSelector: &metav1.LabelSelector{}, + ObjectSelector: &metav1.LabelSelector{}, + SideEffects: &none, + AdmissionReviewVersions: []string{"v1beta1"}, + }}, + } +}