Skip to content

Commit

Permalink
Refactor Admission Router (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
whwalter authored Feb 6, 2024
1 parent 23efc51 commit 668a1e5
Show file tree
Hide file tree
Showing 9 changed files with 235 additions and 73 deletions.
37 changes: 0 additions & 37 deletions examples/k8s/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -173,42 +173,5 @@ webhooks:
- CREATE
resources:
- sensors
scope: "Namespaced"
---
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
creationTimestamp: null
name: argoslower-eventsource
annotations:
cert-manager.io/inject-ca-from: argo/argoslower
webhooks:
- clientConfig:
caBundle: Cg==
service:
name: argoslower
path: /mutate/eventsource
port: 8443
namespace: "argo"
sideEffects: None
admissionReviewVersions: ["v1", "v1beta1"]
failurePolicy: Ignore
name: v1beta1.argoslower.kanopy-platform.github.io
reinvocationPolicy: IfNeeded
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values:
- kube-system
rules:
- apiGroups:
- "argoproj.io"
apiVersions:
- "*"
operations:
- UPDATE
- CREATE
resources:
- eventsources
scope: "Namespaced"
6 changes: 2 additions & 4 deletions internal/admission/eventsource/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,19 +54,17 @@ func (h *Handler) InjectDecoder(decoder *admission.Decoder) error {
func (h *Handler) Handle(ctx context.Context, req admission.Request) admission.Response {
log := log.FromContext(ctx)

log.Info(fmt.Sprintf("Received request: %v", req))

out := &esv1alpha1.EventSource{}

log.Info("Looking for annotation")
log.V(1).Info("Looking for annotation")
if err := h.decoder.Decode(req, out); err != nil {
log.Error(err, fmt.Sprintf("failed to decode eventsource request: %s", req.Name))
return admission.Errored(http.StatusBadRequest, err)
}

_, ok := out.Annotations[h.annotationKey]
if !ok {
log.Info("Annotation not found, ignoring eventsource")
log.V(1).Info("Annotation not found, ignoring eventsource")
return admission.Allowed("No modifications needed")
}

Expand Down
13 changes: 3 additions & 10 deletions internal/admission/eventsource/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"testing"

"github.com/kanopy-platform/argoslower/internal/admission/eventsource"
estest "github.com/kanopy-platform/argoslower/internal/admission/eventsource/testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
admissionv1 "k8s.io/api/admission/v1"
Expand All @@ -20,19 +22,10 @@ import (
esv1alpha1 "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1"
)

type FakeMeshChecker struct {
Mesh bool
Err error
}

func (m *FakeMeshChecker) OnMesh(ns string) (bool, error) {
return m.Mesh, m.Err
}

func TestEventSourceHandler(t *testing.T) {

t.Parallel()
fmc := &FakeMeshChecker{
fmc := &estest.FakeMeshChecker{
Mesh: true,
}

Expand Down
10 changes: 10 additions & 0 deletions internal/admission/eventsource/testing/fakes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package testing

type FakeMeshChecker struct {
Mesh bool
Err error
}

func (m *FakeMeshChecker) OnMesh(ns string) (bool, error) {
return m.Mesh, m.Err
}
65 changes: 65 additions & 0 deletions internal/admission/handler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package admission

import (
"context"
"fmt"

aes "github.com/argoproj/argo-events/pkg/apis/eventsource"
as "github.com/argoproj/argo-events/pkg/apis/sensor"
event "github.com/kanopy-platform/argoslower/internal/admission/eventsource"
sensor "github.com/kanopy-platform/argoslower/internal/admission/sensor"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/webhook"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

type RoutingHandler struct {
sensorHandler *sensor.Handler
eventSourceHandler *event.Handler
}

func NewRoutingHandler(sh *sensor.Handler, es *event.Handler) *RoutingHandler {
return &RoutingHandler{
sensorHandler: sh,
eventSourceHandler: es,
}
}

func (h *RoutingHandler) Handle(ctx context.Context, req admission.Request) admission.Response {
kind := req.RequestKind
if kind == nil {
kind = &req.Kind
}

switch {
case kind.Kind == aes.Kind && h.eventSourceHandler != nil:
return h.eventSourceHandler.Handle(ctx, req)
case kind.Kind == as.Kind && h.sensorHandler != nil:
return h.sensorHandler.Handle(ctx, req)
default:
return admission.Denied(fmt.Sprintf("Kind %s not supported by controller", kind.Kind))
}

}

func (h *RoutingHandler) InjectDecoder(decoder *admission.Decoder) error {
if h.sensorHandler != nil {
err := h.sensorHandler.InjectDecoder(decoder)
if err != nil {
return err
}
}
if h.eventSourceHandler != nil {
err := h.eventSourceHandler.InjectDecoder(decoder)
if err != nil {
return err
}

}

return nil
}

func (h *RoutingHandler) SetupWithManager(m manager.Manager) {
m.GetWebhookServer().Register("/mutate", &webhook.Admission{Handler: h})
}
120 changes: 120 additions & 0 deletions internal/admission/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package admission

import (
"context"
"encoding/json"
"testing"

"github.com/kanopy-platform/argoslower/internal/admission/eventsource"
estest "github.com/kanopy-platform/argoslower/internal/admission/eventsource/testing"
sensor "github.com/kanopy-platform/argoslower/internal/admission/sensor"
stest "github.com/kanopy-platform/argoslower/internal/admission/sensor/testing"
"github.com/kanopy-platform/argoslower/pkg/ratelimit"
"github.com/stretchr/testify/assert"

admissionv1 "k8s.io/api/admission/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"

aes "github.com/argoproj/argo-events/pkg/apis/eventsource"
v1eventsource "github.com/argoproj/argo-events/pkg/apis/eventsource/v1alpha1"
as "github.com/argoproj/argo-events/pkg/apis/sensor"
v1sensor "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
)

func TestRoutingHandler(t *testing.T) {

s := v1sensor.Sensor{
ObjectMeta: v1.ObjectMeta{
Namespace: "test",
Name: "test",
},
Spec: v1sensor.SensorSpec{},
}

sensorBytes, err := json.Marshal(s)
assert.NoError(t, err)

sar := admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: sensorBytes,
},
}

es := v1eventsource.EventSource{
ObjectMeta: v1.ObjectMeta{
Namespace: "test",
Name: "test",
},
Spec: v1eventsource.EventSourceSpec{},
}

eventSourceBytes, err := json.Marshal(es)
assert.NoError(t, err)

esar := admissionv1.AdmissionRequest{
Object: runtime.RawExtension{
Raw: eventSourceBytes,
},
}

frlg := stest.NewFakeRate()
rc := ratelimit.NewRateLimitCalculatorOrDie("Second", int32(80000))

fmc := &estest.FakeMeshChecker{
Mesh: true,
}

tests := map[string]struct {
sensor *sensor.Handler
es *eventsource.Handler
sdeny bool
esdeny bool
}{
"empty": {sdeny: true, esdeny: true},
"sensoronly": {sensor: sensor.NewHandler(&frlg, rc), esdeny: true},
"esonly": {es: eventsource.NewHandler(fmc), sdeny: true},
"both": {sensor: sensor.NewHandler(&frlg, rc), es: eventsource.NewHandler(fmc)},
}

for name, test := range tests {

scheme := runtime.NewScheme()
decoder, err := admission.NewDecoder(scheme)

handler := NewRoutingHandler(test.sensor, test.es)
assert.NoError(t, err)

err = handler.InjectDecoder(decoder)
assert.NoError(t, err)

sck := sar.DeepCopy()
skind := v1.GroupVersionKind{
Kind: as.Kind,
}
sck.Kind = skind
sck.RequestKind = &skind

resp := handler.Handle(context.TODO(), admission.Request{AdmissionRequest: *sck})
assert.Equal(t, !test.sdeny, resp.Allowed, name)

sck.RequestKind = nil
resp = handler.Handle(context.TODO(), admission.Request{AdmissionRequest: *sck})
assert.Equal(t, !test.sdeny, resp.Allowed, name)

esck := esar.DeepCopy()
eskind := v1.GroupVersionKind{
Kind: aes.Kind,
}
esck.Kind = eskind
esck.RequestKind = &eskind

resp = handler.Handle(context.TODO(), admission.Request{AdmissionRequest: *esck})
assert.Equal(t, !test.esdeny, resp.Allowed, name)

esck.RequestKind = nil
resp = handler.Handle(context.TODO(), admission.Request{AdmissionRequest: *esck})
assert.Equal(t, !test.esdeny, resp.Allowed, name)
}
}
23 changes: 3 additions & 20 deletions internal/admission/sensor/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"
"testing"

stest "github.com/kanopy-platform/argoslower/internal/admission/sensor/testing"

sensor "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
"github.com/kanopy-platform/argoslower/pkg/ratelimit"
"github.com/stretchr/testify/assert"
Expand All @@ -16,29 +18,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)

type fakeRateLimitGetter struct {
Rates map[string]*sensor.RateLimit
Err error
}

func (frlg *fakeRateLimitGetter) RateLimit(namespace string) (*sensor.RateLimit, error) {
if r, ok := frlg.Rates[namespace]; ok {
return r, nil
} else {
return nil, frlg.Err
}
}

func newFakeRate() fakeRateLimitGetter {
return fakeRateLimitGetter{
Rates: map[string]*sensor.RateLimit{},
}
}

func TestSensorMutationHook(t *testing.T) {

t.Parallel()
frlg := newFakeRate()
frlg := stest.NewFakeRate()

testRate := &sensor.RateLimit{
Unit: "Second",
Expand Down
24 changes: 24 additions & 0 deletions internal/admission/sensor/testing/fakes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package testing

import (
sensor "github.com/argoproj/argo-events/pkg/apis/sensor/v1alpha1"
)

type FakeRateLimitGetter struct {
Rates map[string]*sensor.RateLimit
Err error
}

func (frlg *FakeRateLimitGetter) RateLimit(namespace string) (*sensor.RateLimit, error) {
if r, ok := frlg.Rates[namespace]; ok {
return r, nil
} else {
return nil, frlg.Err
}
}

func NewFakeRate() FakeRateLimitGetter {
return FakeRateLimitGetter{
Rates: map[string]*sensor.RateLimit{},
}
}
Loading

0 comments on commit 668a1e5

Please sign in to comment.