Skip to content

Commit 8623a9b

Browse files
committed
Add per-webhook logging
This gives each webhook a log, so that serving errors can be tied to a particuluar webhook.
1 parent 06ceb4a commit 8623a9b

File tree

6 files changed

+52
-14
lines changed

6 files changed

+52
-14
lines changed

example/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ func main() {
8888
}
8989

9090
entryLog.Info("registering webhooks to the webhook server")
91-
hookServer.Register("/mutate-pods", webhook.Admission{Handler: &podAnnotator{}})
92-
hookServer.Register("/validate-pods", webhook.Admission{Handler: &podValidator{}})
91+
hookServer.Register("/mutate-pods", &webhook.Admission{Handler: &podAnnotator{}})
92+
hookServer.Register("/validate-pods", &webhook.Admission{Handler: &podValidator{}})
9393

9494
entryLog.Info("starting manager")
9595
if err := mgr.Start(signals.SetupSignalHandler()); err != nil {

pkg/runtime/inject/inject.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ limitations under the License.
1717
package inject
1818

1919
import (
20+
"github.com/go-logr/logr"
2021
"k8s.io/apimachinery/pkg/api/meta"
2122
"k8s.io/apimachinery/pkg/runtime"
2223
"k8s.io/client-go/rest"
24+
2325
"sigs.k8s.io/controller-runtime/pkg/cache"
2426
"sigs.k8s.io/controller-runtime/pkg/client"
2527
)
@@ -129,3 +131,18 @@ func InjectorInto(f Func, i interface{}) (bool, error) {
129131
}
130132
return false, nil
131133
}
134+
135+
// Logger is used to inject Loggers into components that need them
136+
// and don't otherwise have opinions.
137+
type Logger interface {
138+
InjectLogger(l logr.Logger) error
139+
}
140+
141+
// LoggerInto will set the logger on the given object if it implements inject.Logger,
142+
// returning true if a InjectLogger was called, and false otherwise.
143+
func LoggerInto(l logr.Logger, i interface{}) (bool, error) {
144+
if injectable, wantsLogger := i.(Logger); wantsLogger {
145+
return true, injectable.InjectLogger(l)
146+
}
147+
return false, nil
148+
}

pkg/webhook/admission/http.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,21 @@ func init() {
4141

4242
var _ http.Handler = &Webhook{}
4343

44-
func (wh Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
44+
func (wh *Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
4545
var body []byte
4646
var err error
4747

4848
var reviewResponse Response
4949
if r.Body != nil {
5050
if body, err = ioutil.ReadAll(r.Body); err != nil {
51-
log.Error(err, "unable to read the body from the incoming request")
51+
wh.log.Error(err, "unable to read the body from the incoming request")
5252
reviewResponse = Errored(http.StatusBadRequest, err)
5353
wh.writeResponse(w, reviewResponse)
5454
return
5555
}
5656
} else {
5757
err = errors.New("request body is empty")
58-
log.Error(err, "bad request")
58+
wh.log.Error(err, "bad request")
5959
reviewResponse = Errored(http.StatusBadRequest, err)
6060
wh.writeResponse(w, reviewResponse)
6161
return
@@ -65,7 +65,7 @@ func (wh Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
6565
contentType := r.Header.Get("Content-Type")
6666
if contentType != "application/json" {
6767
err = fmt.Errorf("contentType=%s, expected application/json", contentType)
68-
log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
68+
wh.log.Error(err, "unable to process a request with an unknown content type", "content type", contentType)
6969
reviewResponse = Errored(http.StatusBadRequest, err)
7070
wh.writeResponse(w, reviewResponse)
7171
return
@@ -77,7 +77,7 @@ func (wh Webhook) ServeHTTP(w http.ResponseWriter, r *http.Request) {
7777
Request: &req.AdmissionRequest,
7878
}
7979
if _, _, err := admissionCodecs.UniversalDeserializer().Decode(body, nil, &ar); err != nil {
80-
log.Error(err, "unable to decode the request")
80+
wh.log.Error(err, "unable to decode the request")
8181
reviewResponse = Errored(http.StatusBadRequest, err)
8282
wh.writeResponse(w, reviewResponse)
8383
return
@@ -95,7 +95,7 @@ func (wh *Webhook) writeResponse(w io.Writer, response Response) {
9595
}
9696
err := encoder.Encode(responseAdmissionReview)
9797
if err != nil {
98-
log.Error(err, "unable to encode the response")
98+
wh.log.Error(err, "unable to encode the response")
9999
wh.writeResponse(w, Errored(http.StatusInternalServerError, err))
100100
}
101101
}

pkg/webhook/admission/http_test.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,25 @@ import (
2626
. "github.com/onsi/ginkgo"
2727
. "github.com/onsi/gomega"
2828

29+
"sigs.k8s.io/controller-runtime/pkg/runtime/inject"
30+
2931
admissionv1beta1 "k8s.io/api/admission/v1beta1"
3032
)
3133

3234
var _ = Describe("Admission Webhooks", func() {
3335

3436
Describe("HTTP Handler", func() {
3537
var respRecorder *httptest.ResponseRecorder
38+
webhook := &Webhook{
39+
Handler: nil,
40+
}
3641
BeforeEach(func() {
3742
respRecorder = &httptest.ResponseRecorder{
3843
Body: bytes.NewBuffer(nil),
3944
}
45+
_, err := inject.LoggerInto(log.WithName("test-webhook"), webhook)
46+
Expect(err).NotTo(HaveOccurred())
4047
})
41-
webhook := &Webhook{
42-
Handler: nil,
43-
}
4448

4549
It("should return bad-request when given an empty body", func() {
4650
req := &http.Request{Body: nil}

pkg/webhook/admission/webhook.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"net/http"
2323

2424
"github.com/appscode/jsonpatch"
25+
"github.com/go-logr/logr"
2526
admissionv1beta1 "k8s.io/api/admission/v1beta1"
2627
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"k8s.io/apimachinery/pkg/runtime"
@@ -110,8 +111,16 @@ type Webhook struct {
110111
// and potentially patches to apply to the handler.
111112
Handler Handler
112113

113-
// scheme is used to construct the Decoder
114+
// decoder is constructed on receiving a scheme and passed down to then handler
114115
decoder *Decoder
116+
117+
log logr.Logger
118+
}
119+
120+
// InjectLogger gets a handle to a logging instance, hopefully with more info about this particular webhook.
121+
func (w *Webhook) InjectLogger(l logr.Logger) error {
122+
w.log = l
123+
return nil
115124
}
116125

117126
// Handle processes AdmissionRequest.
@@ -121,7 +130,7 @@ type Webhook struct {
121130
func (w *Webhook) Handle(ctx context.Context, req Request) Response {
122131
resp := w.Handler.Handle(ctx, req)
123132
if err := resp.Complete(req); err != nil {
124-
log.Error(err, "unable to encode response")
133+
w.log.Error(err, "unable to encode response")
125134
return Errored(http.StatusInternalServerError, errUnableToEncodeResponse)
126135
}
127136

pkg/webhook/server.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,20 @@ func instrumentedHook(path string, hookRaw http.Handler) http.Handler {
111111
func (s *Server) Start(stop <-chan struct{}) error {
112112
s.defaultingOnce.Do(s.setDefaults)
113113

114+
baseHookLog := log.WithName("webhooks")
114115
// inject fields here as opposed to in Register so that we're certain to have our setFields
115116
// function available.
116-
for _, webhook := range s.webhooks {
117+
for hookPath, webhook := range s.webhooks {
117118
if err := s.setFields(webhook); err != nil {
118119
return err
119120
}
121+
122+
// NB(directxman12): we don't propagate this further by wrapping setFields because it's
123+
// unclear if this is how we want to deal with log propagation. In this specific instance,
124+
// we want to be able to pass a logger to webhooks because they don't know their own path.
125+
if _, err := inject.LoggerInto(baseHookLog.WithValues("webhook", hookPath), webhook); err != nil {
126+
return err
127+
}
120128
}
121129

122130
// TODO: watch the cert dir. Reload the cert if it changes

0 commit comments

Comments
 (0)