github.com/argoproj/argo-events@v1.9.1/webhook/webhook_test.go (about) 1 package webhook 2 3 import ( 4 "context" 5 "crypto/tls" 6 "fmt" 7 "net" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 admissionv1 "k8s.io/api/admission/v1" 12 admissionregistrationv1 "k8s.io/api/admissionregistration/v1" 13 appsv1 "k8s.io/api/apps/v1" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/runtime" 16 "k8s.io/apimachinery/pkg/runtime/schema" 17 fakeClient "k8s.io/client-go/kubernetes/fake" 18 "sigs.k8s.io/controller-runtime/pkg/manager/signals" 19 20 "github.com/argoproj/argo-events/common/logging" 21 eventbusv1alphal1 "github.com/argoproj/argo-events/pkg/apis/eventbus/v1alpha1" 22 eventsourcev1alphal1 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1" 23 sensorv1alphal1 "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1" 24 ) 25 26 func fakeOptions() Options { 27 return Options{ 28 Namespace: "test-ns", 29 DeploymentName: "events-webhook", 30 ClusterRoleName: "argo-events-webhook", 31 ServiceName: "webhook", 32 Port: 8443, 33 SecretName: "webhook-certs", 34 WebhookName: "webhook.argo-events.argoproj.io", 35 } 36 } 37 38 func fakeValidatingWebhookConfig(opts Options) *admissionregistrationv1.ValidatingWebhookConfiguration { 39 sideEffects := admissionregistrationv1.SideEffectClassNone 40 failurePolicy := admissionregistrationv1.Ignore 41 return &admissionregistrationv1.ValidatingWebhookConfiguration{ 42 ObjectMeta: metav1.ObjectMeta{ 43 Name: opts.WebhookName, 44 }, 45 Webhooks: []admissionregistrationv1.ValidatingWebhook{ 46 { 47 Name: opts.WebhookName, 48 Rules: []admissionregistrationv1.RuleWithOperations{{}}, 49 SideEffects: &sideEffects, 50 FailurePolicy: &failurePolicy, 51 ClientConfig: admissionregistrationv1.WebhookClientConfig{}, 52 }, 53 }, 54 } 55 } 56 57 func contextWithLogger(t *testing.T) context.Context { 58 t.Helper() 59 return logging.WithLogger(context.Background(), logging.NewArgoEventsLogger()) 60 } 61 62 func contextWithLoggerAndCancel(t *testing.T) context.Context { 63 t.Helper() 64 return logging.WithLogger(signals.SetupSignalHandler(), logging.NewArgoEventsLogger()) 65 } 66 67 func fakeAdmissionController(t *testing.T, options Options) *AdmissionController { 68 t.Helper() 69 ac := &AdmissionController{ 70 Client: fakeClient.NewSimpleClientset(), 71 Options: options, 72 Handlers: map[schema.GroupVersionKind]runtime.Object{ 73 eventbusv1alphal1.SchemaGroupVersionKind: &eventbusv1alphal1.EventBus{}, 74 eventsourcev1alphal1.SchemaGroupVersionKind: &eventsourcev1alphal1.EventSource{}, 75 sensorv1alphal1.SchemaGroupVersionKind: &sensorv1alphal1.Sensor{}, 76 }, 77 Logger: logging.NewArgoEventsLogger(), 78 } 79 return ac 80 } 81 82 func TestConnectAllowed(t *testing.T) { 83 ac := fakeAdmissionController(t, fakeOptions()) 84 t.Run("test CONNECT allowed", func(t *testing.T) { 85 req := &admissionv1.AdmissionRequest{ 86 Operation: admissionv1.Connect, 87 } 88 resp := ac.admit(contextWithLogger(t), req) 89 assert.True(t, resp.Allowed) 90 }) 91 } 92 93 func TestDeleteAllowed(t *testing.T) { 94 ac := fakeAdmissionController(t, fakeOptions()) 95 t.Run("test DELETE allowed", func(t *testing.T) { 96 req := &admissionv1.AdmissionRequest{ 97 Operation: admissionv1.Delete, 98 } 99 resp := ac.admit(contextWithLogger(t), req) 100 assert.True(t, resp.Allowed) 101 }) 102 } 103 104 func TestUnknownKindFails(t *testing.T) { 105 ac := fakeAdmissionController(t, fakeOptions()) 106 107 t.Run("test unknown types", func(t *testing.T) { 108 req := &admissionv1.AdmissionRequest{ 109 Operation: admissionv1.Create, 110 Kind: metav1.GroupVersionKind{ 111 Group: "test.test.test", 112 Version: "v1alpha1", 113 Kind: "Unknown", 114 }, 115 } 116 resp := ac.admit(contextWithLogger(t), req) 117 assert.True(t, !resp.Allowed) 118 }) 119 } 120 121 func TestDefaultClientAuth(t *testing.T) { 122 opts := fakeOptions() 123 assert.Equal(t, opts.ClientAuth, tls.NoClientCert) 124 } 125 126 func createDeployment(ac *AdmissionController) { 127 opts := fakeOptions() 128 deployment := &appsv1.Deployment{ 129 ObjectMeta: metav1.ObjectMeta{ 130 Name: opts.DeploymentName, 131 Namespace: opts.Namespace, 132 }, 133 } 134 _, _ = ac.Client.AppsV1().Deployments(opts.Namespace).Create(context.TODO(), deployment, metav1.CreateOptions{}) 135 } 136 137 func createWebhook(ac *AdmissionController, wh *admissionregistrationv1.ValidatingWebhookConfiguration) { 138 client := ac.Client.AdmissionregistrationV1().ValidatingWebhookConfigurations() 139 _, err := client.Create(context.TODO(), wh, metav1.CreateOptions{}) 140 if err != nil { 141 panic(fmt.Errorf("failed to create test webhook, %w", err)) 142 } 143 } 144 145 func TestRun(t *testing.T) { 146 opts := fakeOptions() 147 ac := fakeAdmissionController(t, opts) 148 createDeployment(ac) 149 webhook := fakeValidatingWebhookConfig(opts) 150 createWebhook(ac, webhook) 151 152 ctx := contextWithLoggerAndCancel(t) 153 go func() { 154 _ = ac.Run(ctx) 155 }() 156 _, err := net.Dial("tcp", fmt.Sprintf(":%d", opts.Port)) 157 assert.NotNil(t, err) 158 } 159 160 func TestConfigureCertWithExistingSecret(t *testing.T) { 161 t.Run("test configure cert with existing secret", func(t *testing.T) { 162 opts := fakeOptions() 163 ac := fakeAdmissionController(t, opts) 164 createDeployment(ac) 165 ctx := contextWithLogger(t) 166 newSecret, err := ac.generateSecret(ctx) 167 assert.Nil(t, err) 168 _, err = ac.Client.CoreV1().Secrets(opts.Namespace).Create(context.TODO(), newSecret, metav1.CreateOptions{}) 169 assert.Nil(t, err) 170 171 tlsConfig, caCert, err := ac.configureCerts(ctx, tls.NoClientCert) 172 assert.Nil(t, err) 173 assert.NotNil(t, tlsConfig) 174 175 expectedCert, err := tls.X509KeyPair(newSecret.Data[secretServerCert], newSecret.Data[secretServerKey]) 176 assert.Nil(t, err) 177 assert.True(t, len(tlsConfig.Certificates) >= 1) 178 179 assert.Equal(t, expectedCert.Certificate, tlsConfig.Certificates[0].Certificate) 180 assert.Equal(t, newSecret.Data[secretCACert], caCert) 181 }) 182 }