From 284bbc8e7e97aa2ca6968b49d27aeb7ca69f3551 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Fri, 29 Apr 2022 09:44:07 +0200 Subject: [PATCH] e2e/webhooks: clean up webhook fixture and merge zero call check --- .../e2e/apibinding/apibinding_webhook_test.go | 320 ++++++------------ test/e2e/conformance/webhook_test.go | 32 +- test/e2e/fixtures/webhook/webhook.go | 85 +++-- 3 files changed, 153 insertions(+), 284 deletions(-) diff --git a/test/e2e/apibinding/apibinding_webhook_test.go b/test/e2e/apibinding/apibinding_webhook_test.go index ed48881340fc..03f641624a9a 100644 --- a/test/e2e/apibinding/apibinding_webhook_test.go +++ b/test/e2e/apibinding/apibinding_webhook_test.go @@ -18,10 +18,11 @@ package apibinding import ( "context" - "sync" + "path/filepath" "testing" "time" + "github.com/kcp-dev/apimachinery/pkg/logicalcluster" "github.com/stretchr/testify/require" v1 "k8s.io/api/admission/v1" @@ -117,52 +118,53 @@ func TestAPIBindingMutatingWebhook(t *testing.T) { codecs := serializer.NewCodecFactory(scheme) deserializer := codecs.UniversalDeserializer() - //Create Test Server and Create Validating Webhook for Cowboys in source cluster. - testWebhook := webhookserver.WebhookServer{ - Response: v1.AdmissionResponse{ - Allowed: true, - }, - ObjectGVK: schema.GroupVersionKind{ - Group: "wildwest.dev", - Version: "v1alpha1", - Kind: "Cowboy", - }, - T: t, - Lock: sync.Mutex{}, - Deserializer: deserializer, - } - - port, err := framework.GetFreePort(t) - require.NoError(t, err, "failed to get free port for test webhook") - testWebhook.StartServer(ctx, server, port) - - t.Logf("Installing webhook into the source workspace") - sideEffect := admissionregistrationv1.SideEffectClassNone - webhook := &admissionregistrationv1.MutatingWebhookConfiguration{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Name: "test-webhook"}, - Webhooks: []admissionregistrationv1.MutatingWebhook{{ - Name: "test-webhook.cowboy.io", - ClientConfig: admissionregistrationv1.WebhookClientConfig{ - URL: testWebhook.GetURL(), - CABundle: cfg.CAData, + t.Logf("Create test server and create mutating webhook for cowboys in both source and target cluster") + testWebhooks := map[logicalcluster.LogicalCluster]*webhookserver.AdmissionWebhookServer{} + for _, cluster := range []logicalcluster.LogicalCluster{sourceWorkspace, targetWorkspace} { + testWebhooks[cluster] = &webhookserver.AdmissionWebhookServer{ + Response: v1.AdmissionResponse{ + Allowed: true, }, - Rules: []admissionregistrationv1.RuleWithOperations{{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Create, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{"wildwest.dev"}, - APIVersions: []string{"v1alpha1"}, - Resources: []string{"cowboys"}, + ObjectGVK: schema.GroupVersionKind{ + Group: "wildwest.dev", + Version: "v1alpha1", + Kind: "Cowboy", + }, + Deserializer: deserializer, + } + port, err := framework.GetFreePort(t) + require.NoError(t, err, "failed to get free port for test webhook") + dirPath := filepath.Dir(server.KubeconfigPath()) + testWebhooks[cluster].StartTLS(t, filepath.Join(dirPath, "apiserver.crt"), filepath.Join(dirPath, "apiserver.key"), port) + + sideEffect := admissionregistrationv1.SideEffectClassNone + url := testWebhooks[cluster].GetURL() + webhook := &admissionregistrationv1.MutatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{Name: "test-webhook"}, + Webhooks: []admissionregistrationv1.MutatingWebhook{{ + Name: "test-webhook.cowboy.io", + ClientConfig: admissionregistrationv1.WebhookClientConfig{ + URL: &url, + CABundle: cfg.CAData, }, + Rules: []admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Create, + }, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"wildwest.dev"}, + APIVersions: []string{"v1alpha1"}, + Resources: []string{"cowboys"}, + }, + }}, + SideEffects: &sideEffect, + AdmissionReviewVersions: []string{"v1"}, }}, - SideEffects: &sideEffect, - AdmissionReviewVersions: []string{"v1"}, - }}, + } + _, err = kubeClusterClient.Cluster(cluster).AdmissionregistrationV1().MutatingWebhookConfigurations().Create(ctx, webhook, metav1.CreateOptions{}) + require.NoError(t, err, "failed to add validating webhook configurations") } - _, err = kubeClusterClient.Cluster(sourceWorkspace).AdmissionregistrationV1().MutatingWebhookConfigurations().Create(ctx, webhook, metav1.CreateOptions{}) - require.NoError(t, err, "failed to add validating webhook configurations") cowboy := v1alpha1.Cowboy{ ObjectMeta: metav1.ObjectMeta{ @@ -178,9 +180,11 @@ func TestAPIBindingMutatingWebhook(t *testing.T) { if err != nil && !errors.IsAlreadyExists(err) { return false } - return testWebhook.Calls >= 1 - + return testWebhooks[sourceWorkspace].Calls() >= 1 }, wait.ForeverTestTimeout, 100*time.Millisecond) + + t.Logf("Check that the in-workspace webhook was NOT called") + require.Zero(t, testWebhooks[targetWorkspace].Calls(), "in-workspace webhook should not have been called") } func TestAPIBindingValidatingWebhook(t *testing.T) { @@ -254,189 +258,53 @@ func TestAPIBindingValidatingWebhook(t *testing.T) { codecs := serializer.NewCodecFactory(scheme) deserializer := codecs.UniversalDeserializer() - //Create Test Server and Create Validating Webhook for Cowboys in source cluster. - testWebhook := webhookserver.WebhookServer{ - Response: v1.AdmissionResponse{ - Allowed: true, - }, - ObjectGVK: schema.GroupVersionKind{ - Group: "wildwest.dev", - Version: "v1alpha1", - Kind: "Cowboy", - }, - T: t, - Lock: sync.Mutex{}, - Deserializer: deserializer, - } - - port, err := framework.GetFreePort(t) - require.NoError(t, err, "failed to get free port for test webhook") - testWebhook.StartServer(ctx, server, port) - - t.Logf("Installing webhook into the source workspace") - sideEffect := admissionregistrationv1.SideEffectClassNone - webhook := &admissionregistrationv1.ValidatingWebhookConfiguration{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Name: "test-webhook"}, - Webhooks: []admissionregistrationv1.ValidatingWebhook{{ - Name: "test-webhook.cowboy.io", - ClientConfig: admissionregistrationv1.WebhookClientConfig{ - URL: testWebhook.GetURL(), - CABundle: cfg.CAData, - }, - Rules: []admissionregistrationv1.RuleWithOperations{{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Create, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{"wildwest.dev"}, - APIVersions: []string{"v1alpha1"}, - Resources: []string{"cowboys"}, - }, - }}, - SideEffects: &sideEffect, - AdmissionReviewVersions: []string{"v1"}, - }}, - } - _, err = kubeClusterClient.Cluster(sourceWorkspace).AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(ctx, webhook, metav1.CreateOptions{}) - require.NoError(t, err, "failed to add validating webhook configurations") - - cowboy := v1alpha1.Cowboy{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: "testing", - }, - Spec: v1alpha1.CowboySpec{}, - } - - // Avoid race condition here by making sure that CRD is served after installing the types into logical clusters - t.Logf("Creating cowboy resource in target logical cluster") - require.Eventually(t, func() bool { - _, err = cowbyClients.Cluster(targetWorkspace).WildwestV1alpha1().Cowboys("default").Create(ctx, &cowboy, metav1.CreateOptions{}) - if err != nil && !errors.IsAlreadyExists(err) { - return false - } - return testWebhook.Calls >= 1 - - }, wait.ForeverTestTimeout, 100*time.Millisecond) -} - -func TestAPIBindingValidatingWebhookNotCalledWhenBound(t *testing.T) { - t.Parallel() - - server := framework.SharedKcpServer(t) - - ctx, cancel := context.WithCancel(context.Background()) - t.Cleanup(cancel) - - orgClusterName := framework.NewOrganizationFixture(t, server) - sourceWorkspace := framework.NewWorkspaceFixture(t, server, orgClusterName, "Universal") - targetWorkspace := framework.NewWorkspaceFixture(t, server, orgClusterName, "Universal") - - cfg := server.DefaultConfig(t) - - kcpClients, err := clientset.NewClusterForConfig(cfg) - require.NoError(t, err, "failed to construct kcp cluster client for server") - - dynamicClients, err := dynamic.NewClusterForConfig(cfg) - require.NoError(t, err, "failed to construct dynamic cluster client for server") - - kubeClusterClient, err := kubernetes.NewClusterForConfig(cfg) - require.NoError(t, err, "failed to construct client for server") - - t.Logf("Install a cowboys APIResourceSchema into workspace %q", sourceWorkspace) - mapper := restmapper.NewDeferredDiscoveryRESTMapper(memory.NewMemCacheClient(kcpClients.Cluster(sourceWorkspace).Discovery())) - err = helpers.CreateResourceFromFS(ctx, dynamicClients.Cluster(sourceWorkspace), mapper, "apiresourceschema_cowboys.yaml", testFiles) - require.NoError(t, err) - - t.Logf("Create an APIExport for it") - cowboysAPIExport := &apisv1alpha1.APIExport{ - ObjectMeta: metav1.ObjectMeta{ - Name: "today-cowboys", - }, - Spec: apisv1alpha1.APIExportSpec{ - LatestResourceSchemas: []string{"today.cowboys.wildwest.dev"}, - }, - } - _, err = kcpClients.Cluster(sourceWorkspace).ApisV1alpha1().APIExports().Create(ctx, cowboysAPIExport, metav1.CreateOptions{}) - require.NoError(t, err) - - t.Logf("Create an APIBinding in workspace %q that points to the today-cowboys export", targetWorkspace) - require.NoError(t, err) - apiBinding := &apisv1alpha1.APIBinding{ - ObjectMeta: metav1.ObjectMeta{ - Name: "cowboys", - }, - Spec: apisv1alpha1.APIBindingSpec{ - Reference: apisv1alpha1.ExportReference{ - Workspace: &apisv1alpha1.WorkspaceExportReference{ - WorkspaceName: sourceWorkspace.Base(), - ExportName: cowboysAPIExport.Name, - }, + t.Logf("Create test server and create validating webhook for cowboys in both source and target cluster") + testWebhooks := map[logicalcluster.LogicalCluster]*webhookserver.AdmissionWebhookServer{} + for _, cluster := range []logicalcluster.LogicalCluster{sourceWorkspace, targetWorkspace} { + testWebhooks[cluster] = &webhookserver.AdmissionWebhookServer{ + Response: v1.AdmissionResponse{ + Allowed: true, }, - }, - } - - _, err = kcpClients.Cluster(targetWorkspace).ApisV1alpha1().APIBindings().Create(ctx, apiBinding, metav1.CreateOptions{}) - require.NoError(t, err) - - scheme := runtime.NewScheme() - err = admissionregistrationv1.AddToScheme(scheme) - require.NoError(t, err, "failed to add admission registration v1 scheme") - err = v1.AddToScheme(scheme) - require.NoError(t, err, "failed to add admission v1 scheme") - err = v1alpha1.AddToScheme(scheme) - require.NoError(t, err, "failed to add cowboy v1alpha1 to scheme") - cowbyClients, err := client.NewClusterForConfig(cfg) - require.NoError(t, err, "failed to add cowboy v1alpha1 to scheme") - codecs := serializer.NewCodecFactory(scheme) - deserializer := codecs.UniversalDeserializer() - - //Create Test Server and Create Validating Webhook for Cowboys in source cluster. - testWebhook := webhookserver.WebhookServer{ - Response: v1.AdmissionResponse{ - Allowed: true, - }, - ObjectGVK: schema.GroupVersionKind{ - Group: "wildwest.dev", - Version: "v1alpha1", - Kind: "Cowboy", - }, - T: t, - Lock: sync.Mutex{}, - Deserializer: deserializer, - } - - port, err := framework.GetFreePort(t) - require.NoError(t, err, "failed to get free port for test webhook") - testWebhook.StartServer(ctx, server, port) - - t.Logf("Installing webhook into the source workspace") - sideEffect := admissionregistrationv1.SideEffectClassNone - webhook := &admissionregistrationv1.ValidatingWebhookConfiguration{ - TypeMeta: metav1.TypeMeta{}, - ObjectMeta: metav1.ObjectMeta{Name: "test-webhook"}, - Webhooks: []admissionregistrationv1.ValidatingWebhook{{ - Name: "test-webhook.cowboy.io", - ClientConfig: admissionregistrationv1.WebhookClientConfig{ - URL: testWebhook.GetURL(), - CABundle: cfg.CAData, + ObjectGVK: schema.GroupVersionKind{ + Group: "wildwest.dev", + Version: "v1alpha1", + Kind: "Cowboy", }, - Rules: []admissionregistrationv1.RuleWithOperations{{ - Operations: []admissionregistrationv1.OperationType{ - admissionregistrationv1.Create, - }, - Rule: admissionregistrationv1.Rule{ - APIGroups: []string{"wildwest.dev"}, - APIVersions: []string{"v1alpha1"}, - Resources: []string{"cowboys"}, + Deserializer: deserializer, + } + port, err := framework.GetFreePort(t) + require.NoError(t, err, "failed to get free port for test webhook") + dirPath := filepath.Dir(server.KubeconfigPath()) + testWebhooks[cluster].StartTLS(t, filepath.Join(dirPath, "apiserver.crt"), filepath.Join(dirPath, "apiserver.key"), port) + + sideEffect := admissionregistrationv1.SideEffectClassNone + url := testWebhooks[cluster].GetURL() + webhook := &admissionregistrationv1.ValidatingWebhookConfiguration{ + TypeMeta: metav1.TypeMeta{}, + ObjectMeta: metav1.ObjectMeta{Name: "test-webhook"}, + Webhooks: []admissionregistrationv1.ValidatingWebhook{{ + Name: "test-webhook.cowboy.io", + ClientConfig: admissionregistrationv1.WebhookClientConfig{ + URL: &url, + CABundle: cfg.CAData, }, + Rules: []admissionregistrationv1.RuleWithOperations{{ + Operations: []admissionregistrationv1.OperationType{ + admissionregistrationv1.Create, + }, + Rule: admissionregistrationv1.Rule{ + APIGroups: []string{"wildwest.dev"}, + APIVersions: []string{"v1alpha1"}, + Resources: []string{"cowboys"}, + }, + }}, + SideEffects: &sideEffect, + AdmissionReviewVersions: []string{"v1"}, }}, - SideEffects: &sideEffect, - AdmissionReviewVersions: []string{"v1"}, - }}, + } + _, err = kubeClusterClient.Cluster(cluster).AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(ctx, webhook, metav1.CreateOptions{}) + require.NoError(t, err, "failed to add validating webhook configurations") } - _, err = kubeClusterClient.Cluster(targetWorkspace).AdmissionregistrationV1().ValidatingWebhookConfigurations().Create(ctx, webhook, metav1.CreateOptions{}) - require.NoError(t, err, "failed to add validating webhook configurations") cowboy := v1alpha1.Cowboy{ ObjectMeta: metav1.ObjectMeta{ @@ -452,7 +320,9 @@ func TestAPIBindingValidatingWebhookNotCalledWhenBound(t *testing.T) { if err != nil && !errors.IsAlreadyExists(err) { return false } - return testWebhook.Calls == 0 - + return testWebhooks[sourceWorkspace].Calls() >= 1 }, wait.ForeverTestTimeout, 100*time.Millisecond) + + t.Logf("Check that the in-workspace webhook was NOT called") + require.Zero(t, testWebhooks[targetWorkspace].Calls(), "in-workspace webhook should not have been called") } diff --git a/test/e2e/conformance/webhook_test.go b/test/e2e/conformance/webhook_test.go index 7ff27c4f2bd1..2be85395f7ab 100644 --- a/test/e2e/conformance/webhook_test.go +++ b/test/e2e/conformance/webhook_test.go @@ -18,7 +18,7 @@ package conformance import ( "context" - "sync" + "path/filepath" "testing" "time" @@ -65,7 +65,7 @@ func TestMutatingWebhookInWorkspace(t *testing.T) { codecs := serializer.NewCodecFactory(scheme) deserializer := codecs.UniversalDeserializer() - testWebhook := webhookserver.WebhookServer{ + testWebhook := webhookserver.AdmissionWebhookServer{ Response: v1.AdmissionResponse{ Allowed: true, }, @@ -74,14 +74,13 @@ func TestMutatingWebhookInWorkspace(t *testing.T) { Version: "v1alpha1", Kind: "Cowboy", }, - T: t, - Lock: sync.Mutex{}, Deserializer: deserializer, } port, err := framework.GetFreePort(t) require.NoError(t, err, "failed to get free port for test webhook") - testWebhook.StartServer(ctx, server, port) + dirPath := filepath.Dir(server.KubeconfigPath()) + testWebhook.StartTLS(t, filepath.Join(dirPath, "apiserver.crt"), filepath.Join(dirPath, "apiserver.key"), port) organization := framework.NewOrganizationFixture(t, server) logicalClusters := []logicalcluster.LogicalCluster{ @@ -105,13 +104,14 @@ func TestMutatingWebhookInWorkspace(t *testing.T) { t.Logf("Installing webhook into the first workspace") sideEffect := admissionregistrationv1.SideEffectClassNone + url := testWebhook.GetURL() webhook := &admissionregistrationv1.MutatingWebhookConfiguration{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{Name: "test-webhook"}, Webhooks: []admissionregistrationv1.MutatingWebhook{{ Name: "test-webhook.cowboy.io", ClientConfig: admissionregistrationv1.WebhookClientConfig{ - URL: testWebhook.GetURL(), + URL: &url, CABundle: cfg.CAData, }, Rules: []admissionregistrationv1.RuleWithOperations{{ @@ -148,7 +148,7 @@ func TestMutatingWebhookInWorkspace(t *testing.T) { return true }, wait.ForeverTestTimeout, 100*time.Millisecond) - require.Equal(t, 0, testWebhook.Calls, "expected that the webhook is not called for logical cluster where webhook is not installed") + require.Equal(t, 0, testWebhook.Calls(), "expected that the webhook is not called for logical cluster where webhook is not installed") t.Logf("Creating cowboy resource in first logical cluster") require.Eventually(t, func() bool { @@ -160,10 +160,10 @@ func TestMutatingWebhookInWorkspace(t *testing.T) { }, wait.ForeverTestTimeout, 100*time.Millisecond) - //Avoid race condition where webhook informer is not updated before the call to create was made. + // Avoid race condition where webhook informer is not updated before the call to create was made. t.Log("Verify webhook is eventually called") require.Eventually(t, func() bool { - return testWebhook.Calls == 1 + return testWebhook.Calls() == 1 }, wait.ForeverTestTimeout, 100*time.Millisecond) } @@ -189,7 +189,7 @@ func TestValidatingWebhookInWorkspace(t *testing.T) { codecs := serializer.NewCodecFactory(scheme) deserializer := codecs.UniversalDeserializer() - testWebhook := webhookserver.WebhookServer{ + testWebhook := webhookserver.AdmissionWebhookServer{ Response: v1.AdmissionResponse{ Allowed: true, }, @@ -198,14 +198,13 @@ func TestValidatingWebhookInWorkspace(t *testing.T) { Version: "v1alpha1", Kind: "Cowboy", }, - T: t, - Lock: sync.Mutex{}, Deserializer: deserializer, } port, err := framework.GetFreePort(t) require.NoError(t, err, "failed to get free port for test webhook") - testWebhook.StartServer(ctx, server, port) + dirPath := filepath.Dir(server.KubeconfigPath()) + testWebhook.StartTLS(t, filepath.Join(dirPath, "apiserver.crt"), filepath.Join(dirPath, "apiserver.key"), port) organization := framework.NewOrganizationFixture(t, server) logicalClusters := []logicalcluster.LogicalCluster{ @@ -229,13 +228,14 @@ func TestValidatingWebhookInWorkspace(t *testing.T) { t.Logf("Installing webhook into the first workspace") sideEffect := admissionregistrationv1.SideEffectClassNone + url := testWebhook.GetURL() webhook := &admissionregistrationv1.ValidatingWebhookConfiguration{ TypeMeta: metav1.TypeMeta{}, ObjectMeta: metav1.ObjectMeta{Name: "test-webhook"}, Webhooks: []admissionregistrationv1.ValidatingWebhook{{ Name: "test-webhook.cowboy.io", ClientConfig: admissionregistrationv1.WebhookClientConfig{ - URL: testWebhook.GetURL(), + URL: &url, CABundle: cfg.CAData, }, Rules: []admissionregistrationv1.RuleWithOperations{{ @@ -272,7 +272,7 @@ func TestValidatingWebhookInWorkspace(t *testing.T) { return true }, wait.ForeverTestTimeout, 100*time.Millisecond) - require.Equal(t, 0, testWebhook.Calls, "expected that the webhook is not called for logical cluster where webhook is not installed") + require.Equal(t, 0, testWebhook.Calls(), "expected that the webhook is not called for logical cluster where webhook is not installed") t.Logf("Creating cowboy resource in first logical cluster") require.Eventually(t, func() bool { @@ -287,6 +287,6 @@ func TestValidatingWebhookInWorkspace(t *testing.T) { //Avoid race condition where webhook informer is not updated before the call to create was made. t.Log("Verify webhook is eventually called") require.Eventually(t, func() bool { - return testWebhook.Calls == 1 + return testWebhook.Calls() == 1 }, wait.ForeverTestTimeout, 100*time.Millisecond) } diff --git a/test/e2e/fixtures/webhook/webhook.go b/test/e2e/fixtures/webhook/webhook.go index 163d23f19c61..adced51aebe8 100644 --- a/test/e2e/fixtures/webhook/webhook.go +++ b/test/e2e/fixtures/webhook/webhook.go @@ -22,71 +22,64 @@ import ( "fmt" "io/ioutil" "net/http" - "path/filepath" "sync" "testing" v1 "k8s.io/api/admission/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - - "github.com/kcp-dev/kcp/test/e2e/framework" ) -type WebhookServer struct { +type AdmissionWebhookServer struct { Response v1.AdmissionResponse ObjectGVK schema.GroupVersionKind - T *testing.T Deserializer runtime.Decoder - port string + t *testing.T - Lock sync.Mutex - Calls int + port string + lock sync.Mutex + calls int } -func (t *WebhookServer) StartServer(ctx context.Context, server framework.RunningServer, port string) { - dirPath := filepath.Dir(server.KubeconfigPath()) - // using known path to cert and key - cfg := server.DefaultConfig(t.T) - cfg.CertFile = filepath.Join(dirPath, "apiserver.crt") - cfg.KeyFile = filepath.Join(dirPath, "apiserver.key") - t.port = port - serv := &http.Server{Addr: fmt.Sprintf(":%v", port), Handler: t} - go func() { - <-ctx.Done() +func (s *AdmissionWebhookServer) StartTLS(t *testing.T, certFile, keyFile string, port string) { + s.t = t + s.port = port + + serv := &http.Server{Addr: fmt.Sprintf(":%v", port), Handler: s} + t.Cleanup(func() { fmt.Printf("Shutting down the HTTP server") err := serv.Shutdown(context.TODO()) if err != nil { fmt.Printf("unable to shutdown server gracefully err: %v", err) } - }() + }) + go func() { - err := serv.ListenAndServeTLS(cfg.CertFile, cfg.KeyFile) + err := serv.ListenAndServeTLS(certFile, keyFile) if err != nil && err != http.ErrServerClosed { fmt.Printf("unable to shutdown server gracefully err: %v", err) } }() } -func (t *WebhookServer) GetURL() *string { - s := fmt.Sprintf("https://localhost:%v/hello", t.port) - return &s +func (s *AdmissionWebhookServer) GetURL() string { + return fmt.Sprintf("https://localhost:%v/hello", s.port) } -func (t *WebhookServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { +func (s *AdmissionWebhookServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { // Make sure that this is a request for the object that was set. - t.T.Log("made it webhook") + s.t.Log("made it webhook") if req.Body == nil { msg := "Expected request body to be non-empty" - t.T.Logf("%v", msg) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) } data, err := ioutil.ReadAll(req.Body) if err != nil { msg := fmt.Sprintf("Request could not be decoded: %v", err) - t.T.Logf("%v", msg) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) } @@ -94,22 +87,22 @@ func (t *WebhookServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { contentType := req.Header.Get("Content-Type") if contentType != "application/json" { msg := fmt.Sprintf("contentType=%s, expect application/json", contentType) - t.T.Logf("%v", msg) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) return } - obj, gvk, err := t.Deserializer.Decode(data, nil, nil) + obj, gvk, err := s.Deserializer.Decode(data, nil, nil) if err != nil { msg := fmt.Sprintf("Unable to decode object: %v", err) - t.T.Logf("%v", msg) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) return } if *gvk != v1.SchemeGroupVersion.WithKind("AdmissionReview") { msg := fmt.Sprintf("Expected AdmissionReview but got: %T", obj) - t.T.Logf("%v", msg) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) return } @@ -117,22 +110,22 @@ func (t *WebhookServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { if !ok { //return an error msg := fmt.Sprintf("Expected AdmissionReview but got: %T", obj) - t.T.Logf("%v", msg) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) return } - obj, objGVK, err := t.Deserializer.Decode(requestedAdmissionReview.Request.Object.Raw, nil, nil) + obj, objGVK, err := s.Deserializer.Decode(requestedAdmissionReview.Request.Object.Raw, nil, nil) if err != nil { msg := fmt.Sprintf("Unable to decode admissions reqeusted object: %v", err) - t.T.Logf("%v", msg) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) return } - if t.ObjectGVK != *objGVK { + if s.ObjectGVK != *objGVK { //return an error - msg := fmt.Sprintf("Expected ObjectGVK: %v but got: %T", t.ObjectGVK, obj) - t.T.Logf("%v", msg) + msg := fmt.Sprintf("Expected ObjectGVK: %v but got: %T", s.ObjectGVK, obj) + s.t.Logf("%v", msg) http.Error(resp, msg, http.StatusBadRequest) return } @@ -140,22 +133,28 @@ func (t *WebhookServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { responseAdmissionReview := &v1.AdmissionReview{ TypeMeta: requestedAdmissionReview.TypeMeta, } - responseAdmissionReview.Response = &t.Response + responseAdmissionReview.Response = &s.Response responseAdmissionReview.Response.UID = requestedAdmissionReview.Request.UID respBytes, err := json.Marshal(responseAdmissionReview) if err != nil { - t.T.Logf("%v", err) + s.t.Logf("%v", err) http.Error(resp, err.Error(), http.StatusInternalServerError) return } - t.Lock.Lock() - t.Calls = t.Calls + 1 - t.Lock.Unlock() + s.lock.Lock() + defer s.lock.Unlock() + s.calls = s.calls + 1 resp.Header().Set("Content-Type", "application/json") if _, err := resp.Write(respBytes); err != nil { - t.T.Logf("%v", err) + s.t.Logf("%v", err) } } + +func (s *AdmissionWebhookServer) Calls() int { + s.lock.Lock() + defer s.lock.Unlock() + return s.calls +}