Skip to content

Commit

Permalink
Rework logger and pass *Request pointer in webhook/authentication
Browse files Browse the repository at this point in the history
Signed-off-by: Vince Prignano <vincepri@redhat.com>
  • Loading branch information
vincepri committed Jan 20, 2023
1 parent 0d4b81e commit 2493b39
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 39 deletions.
2 changes: 1 addition & 1 deletion examples/tokenreview/tokenreview.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ type authenticator struct {
}

// authenticator admits a request by the token.
func (a *authenticator) Handle(ctx context.Context, req authentication.Request) authentication.Response {
func (a *authenticator) Handle(ctx context.Context, req *authentication.Request) authentication.Response {
if req.Spec.Token == "invalid" {
return authentication.Unauthenticated("invalid is an invalid token", v1.UserInfo{})
}
Expand Down
6 changes: 0 additions & 6 deletions pkg/webhook/admission/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
machinerytypes "k8s.io/apimachinery/pkg/types"

internallog "sigs.k8s.io/controller-runtime/pkg/internal/log"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
)
Expand Down Expand Up @@ -59,7 +58,6 @@ var _ = Describe("Admission Webhooks", func() {
}
webhook := &Webhook{
Handler: handler,
log: internallog.RuntimeLog.WithName("webhook"),
}

return webhook
Expand Down Expand Up @@ -109,7 +107,6 @@ var _ = Describe("Admission Webhooks", func() {
},
}
}),
log: internallog.RuntimeLog.WithName("webhook"),
}

By("invoking the webhook")
Expand All @@ -126,7 +123,6 @@ var _ = Describe("Admission Webhooks", func() {
Handler: HandlerFunc(func(ctx context.Context, req Request) Response {
return Patched("", jsonpatch.Operation{Operation: "add", Path: "/a", Value: 2}, jsonpatch.Operation{Operation: "replace", Path: "/b", Value: 4})
}),
log: internallog.RuntimeLog.WithName("webhook"),
}

By("invoking the webhook")
Expand Down Expand Up @@ -213,7 +209,6 @@ var _ = Describe("Admission Webhooks", func() {
webhook := &Webhook{
Handler: handler,
RecoverPanic: true,
log: internallog.RuntimeLog.WithName("webhook"),
}

return webhook
Expand All @@ -240,7 +235,6 @@ var _ = Describe("Admission Webhooks", func() {
}
webhook := &Webhook{
Handler: handler,
log: internallog.RuntimeLog.WithName("webhook"),
}

return webhook
Expand Down
6 changes: 0 additions & 6 deletions pkg/webhook/authentication/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,3 @@ methods to implement authentication webhook handlers.
See examples/tokenreview/ for an example of authentication webhooks.
*/
package authentication

import (
logf "sigs.k8s.io/controller-runtime/pkg/internal/log"
)

var log = logf.RuntimeLog.WithName("authentication")
20 changes: 10 additions & 10 deletions pkg/webhook/authentication/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var reviewResponse Response
if r.Body == nil {
err = errors.New("request body is empty")
log.Error(err, "bad request")
wh.getLogger(nil).Error(err, "bad request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
}

defer r.Body.Close()
if body, err = io.ReadAll(r.Body); err != nil {
log.Error(err, "unable to read the body from the incoming request")
wh.getLogger(nil).Error(err, "unable to read the body from the incoming request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
Expand All @@ -69,7 +69,7 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// verify the content type is accurate
if contentType := r.Header.Get("Content-Type"); contentType != "application/json" {
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
wh.getLogger(nil).Error(err, "unable to process a request with an unknown content type", "content type", contentType)
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
Expand All @@ -82,23 +82,23 @@ func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// unregistered type to the v1 GVK, the decoder will coerce a v1beta1 TokenReview to authenticationv1.
// The actual TokenReview GVK will be used to write a typed response in case the
// webhook config permits multiple versions, otherwise this response will fail.
req := Request{}
req := new(Request)
ar := unversionedTokenReview{}
// avoid an extra copy
ar.TokenReview = &req.TokenReview
ar.SetGroupVersionKind(authenticationv1.SchemeGroupVersion.WithKind("TokenReview"))
_, actualTokRevGVK, err := authenticationCodecs.UniversalDeserializer().Decode(body, nil, &ar)
if err != nil {
log.Error(err, "unable to decode the request")
wh.getLogger(req).Error(err, "unable to decode the request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
}
log.V(1).Info("received request", "UID", req.UID, "kind", req.Kind)
wh.getLogger(req).V(1).Info("received request", "UID", req.UID, "kind", req.Kind)

if req.Spec.Token == "" {
err = errors.New("token is empty")
log.Error(err, "bad request")
wh.getLogger(req).Error(err, "bad request")
reviewResponse = Errored(err)
wh.writeResponse(w, reviewResponse)
return
Expand Down Expand Up @@ -131,12 +131,12 @@ func (wh *Webhook) writeResponseTyped(w io.Writer, response Response, tokRevGVK
// writeTokenResponse writes ar to w.
func (wh *Webhook) writeTokenResponse(w io.Writer, ar authenticationv1.TokenReview) {
if err := json.NewEncoder(w).Encode(ar); err != nil {
log.Error(err, "unable to encode the response")
wh.getLogger(nil).Error(err, "unable to encode the response")
wh.writeResponse(w, Errored(err))
}
res := ar
if log.V(1).Enabled() {
log.V(1).Info("wrote response", "UID", res.UID, "authenticated", res.Status.Authenticated)
if wh.getLogger(nil).V(1).Enabled() {
wh.getLogger(nil).V(1).Info("wrote response", "UID", res.UID, "authenticated", res.Status.Authenticated)
}
}

Expand Down
8 changes: 4 additions & 4 deletions pkg/webhook/authentication/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ var _ = Describe("Authentication Webhooks", func() {
const value = "from-ctx"
webhook := &Webhook{
Handler: &fakeHandler{
fn: func(ctx context.Context, req Request) Response {
fn: func(ctx context.Context, req *Request) Response {
<-ctx.Done()
return Authenticated(ctx.Value(key).(string), authenticationv1.UserInfo{})
},
Expand All @@ -164,7 +164,7 @@ var _ = Describe("Authentication Webhooks", func() {
const key ctxkey = 1
webhook := &Webhook{
Handler: &fakeHandler{
fn: func(ctx context.Context, req Request) Response {
fn: func(ctx context.Context, req *Request) Response {
return Authenticated(ctx.Value(key).(string), authenticationv1.UserInfo{})
},
},
Expand Down Expand Up @@ -192,10 +192,10 @@ func (nopCloser) Close() error { return nil }

type fakeHandler struct {
invoked bool
fn func(context.Context, Request) Response
fn func(context.Context, *Request) Response
}

func (h *fakeHandler) Handle(ctx context.Context, req Request) Response {
func (h *fakeHandler) Handle(ctx context.Context, req *Request) Response {
h.invoked = true
if h.fn != nil {
return h.fn(ctx, req)
Expand Down
42 changes: 36 additions & 6 deletions pkg/webhook/authentication/webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ import (
"context"
"errors"
"net/http"
"sync"

"github.com/go-logr/logr"
authenticationv1 "k8s.io/api/authentication/v1"
"k8s.io/klog/v2"

logf "sigs.k8s.io/controller-runtime/pkg/log"
)

var (
Expand All @@ -46,7 +51,7 @@ type Response struct {

// Complete populates any fields that are yet to be set in
// the underlying TokenResponse, It mutates the response.
func (r *Response) Complete(req Request) error {
func (r *Response) Complete(req *Request) error {
r.UID = req.UID

return nil
Expand All @@ -58,16 +63,16 @@ type Handler interface {
//
// The supplied context is extracted from the received http.Request, allowing wrapping
// http.Handlers to inject values into and control cancelation of downstream request processing.
Handle(context.Context, Request) Response
Handle(context.Context, *Request) Response
}

// HandlerFunc implements Handler interface using a single function.
type HandlerFunc func(context.Context, Request) Response
type HandlerFunc func(context.Context, *Request) Response

var _ Handler = HandlerFunc(nil)

// Handle process the TokenReview by invoking the underlying function.
func (f HandlerFunc) Handle(ctx context.Context, req Request) Response {
func (f HandlerFunc) Handle(ctx context.Context, req *Request) Response {
return f(ctx, req)
}

Expand All @@ -81,15 +86,40 @@ type Webhook struct {
// add any additional information such as passing the request path or
// headers thus allowing you to read them from within the handler
WithContextFunc func(context.Context, *http.Request) context.Context

setupLogOnce sync.Once
log logr.Logger
}

// Handle processes TokenReview.
func (wh *Webhook) Handle(ctx context.Context, req Request) Response {
func (wh *Webhook) Handle(ctx context.Context, req *Request) Response {
resp := wh.Handler.Handle(ctx, req)
if err := resp.Complete(req); err != nil {
log.Error(err, "unable to encode response")
wh.getLogger(req).Error(err, "unable to encode response")
return Errored(errUnableToEncodeResponse)
}

return resp
}

// getLogger constructs a logger from the injected log and LogConstructor.
func (wh *Webhook) getLogger(req *Request) logr.Logger {
wh.setupLogOnce.Do(func() {
if wh.log.GetSink() == nil {
wh.log = logf.Log.WithName("authentication")
}
})

return logConstructor(wh.log, req)
}

// logConstructor adds some commonly interesting fields to the given logger.
func logConstructor(base logr.Logger, req *Request) logr.Logger {
if req != nil {
return base.WithValues("object", klog.KRef(req.Namespace, req.Name),
"namespace", req.Namespace, "name", req.Name,
"user", req.Status.User.Username,
)
}
return base
}
12 changes: 6 additions & 6 deletions pkg/webhook/authentication/webhook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import (
var _ = Describe("Authentication Webhooks", func() {
allowHandler := func() *Webhook {
handler := &fakeHandler{
fn: func(ctx context.Context, req Request) Response {
fn: func(ctx context.Context, req *Request) Response {
return Response{
TokenReview: authenticationv1.TokenReview{
Status: authenticationv1.TokenReviewStatus{
Expand All @@ -52,7 +52,7 @@ var _ = Describe("Authentication Webhooks", func() {
webhook := allowHandler()

By("invoking the webhook")
resp := webhook.Handle(context.Background(), Request{})
resp := webhook.Handle(context.Background(), &Request{})

By("checking that it allowed the request")
Expect(resp.Status.Authenticated).To(BeTrue())
Expand All @@ -63,7 +63,7 @@ var _ = Describe("Authentication Webhooks", func() {
webhook := allowHandler()

By("invoking the webhook")
resp := webhook.Handle(context.Background(), Request{TokenReview: authenticationv1.TokenReview{ObjectMeta: metav1.ObjectMeta{UID: "foobar"}}})
resp := webhook.Handle(context.Background(), &Request{TokenReview: authenticationv1.TokenReview{ObjectMeta: metav1.ObjectMeta{UID: "foobar"}}})

By("checking that the response share's the request's UID")
Expect(resp.UID).To(Equal(machinerytypes.UID("foobar")))
Expand All @@ -74,7 +74,7 @@ var _ = Describe("Authentication Webhooks", func() {
webhook := allowHandler()

By("invoking the webhook")
resp := webhook.Handle(context.Background(), Request{})
resp := webhook.Handle(context.Background(), &Request{})

By("checking that the response share's the request's UID")
Expect(resp.Status).To(Equal(authenticationv1.TokenReviewStatus{Authenticated: true}))
Expand All @@ -83,7 +83,7 @@ var _ = Describe("Authentication Webhooks", func() {
It("shouldn't overwrite the status on a response", func() {
By("setting up a webhook that sets a status")
webhook := &Webhook{
Handler: HandlerFunc(func(ctx context.Context, req Request) Response {
Handler: HandlerFunc(func(ctx context.Context, req *Request) Response {
return Response{
TokenReview: authenticationv1.TokenReview{
Status: authenticationv1.TokenReviewStatus{
Expand All @@ -96,7 +96,7 @@ var _ = Describe("Authentication Webhooks", func() {
}

By("invoking the webhook")
resp := webhook.Handle(context.Background(), Request{})
resp := webhook.Handle(context.Background(), &Request{})

By("checking that the message is intact")
Expect(resp.Status).NotTo(BeNil())
Expand Down

0 comments on commit 2493b39

Please sign in to comment.