Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

apiserver: decorate http.ResponseWriter correctly #104920

Merged
merged 2 commits into from
Oct 5, 2021

Conversation

tkashem
Copy link
Contributor

@tkashem tkashem commented Sep 10, 2021

What type of PR is this?

/kind bug

What this PR does / why we need it:

today there are four handlers that decoratehttp.ResponseWriter in apiserver filter chain:

Additional interfaces like http.Flusher, http.Hijacker, http.CloseNotifier are also implemented by the basic response writer object provided to the request handler by net/http.

we want to establish an invariant that the decorator extends only the interfaces implemented by the original response writer object, so that check like the following always gives the correct answer:

https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L177-L183

	flusher, ok := w.(http.Flusher)
	if !ok {
		err := fmt.Errorf("unable to start watch - can't get http.Flusher: %#v", w)
		utilruntime.HandleError(err)
		s.Scope.err(errors.NewInternalError(err), w, req)
		return
	}

our decorators have logic like this (https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go#L490-L498)

func(w http.ResponseWriter, req *http.Request) {
                delegate := &ResponseWriterDelegator{ResponseWriter: w}

		_, closenotifiable := response.ResponseWriter.(http.CloseNotifier)
		_, flushable := response.ResponseWriter.(http.Flusher)
		_, hijackable := response.ResponseWriter.(http.Hijacker)
		var rw http.ResponseWriter
		if closenotifiable && flushable && hijackable {
			rw = &fancyResponseWriterDelegator{delegate}
		} else {
			rw = delegate
		}
}

for an http2 request, if closenotifiable && flushable && hijackable will never be true, since http2 does not implement http.Hijacker. The following is from go net/http doc:

The default ResponseWriter for HTTP/1.x connections supports Hijacker, but HTTP/2 connections 
intentionally do not. Handlers should always test for this ability at runtime.
  • http/1.x request: closenotifiable && flushable && hijackable is true
  • http2 request: closenotifiable && flushable is true

so the decorator drops Flusher and we see the following error for watch request if the decorator is used:

E0824 18:47:56.493042   56565 watch.go:180] unable to start watch - can't get http.Flusher: 
&metrics.ResponseWriterDelegator{ResponseWriter:(*http2.responseWriter)(0xc00efb3e30), status:0, written:0, 
wroteHeader:false}

Today, watch.go works around this issue by skipping the decorators and looking at the original response writer object provided by net/http
https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go#L169

w = httplog.Unlogged(req, w)

This poses a couple of issues:

  • we always need to initialize httplog constructs even though log level is below 3. This is blocking apiserver: construct logger for httplog only when log level is 3 #104557. We basically want to optimize httplog - initialize only when we need to.
  • also, status code for watch requests do not show up in audit log or metric since the decorators are bypassed. Following is an output of request counter for WATCH :
apiserver_request_total{code="0",component="apiserver",dry_run="",group="",resource="configmaps",
scope="namespace",subresource="",verb="WATCH",version="v1"} 37

code is always 0 (we could not tap into the status code because decorators were skipped)

This PR ensures that:

  • decorators do not "dummy" implement any interface that is not implemented by the original response writer object
  • the handler can reliably do check similar to flusher, ok := w.(http.Flusher), we won't need to retrieve the original response writer object.
  • all decorators get executed as intended (because they will no longer be skipped).

Which issue(s) this PR fixes:

Special notes for your reviewer:

If folks have an interest to have a reusable abstraction for this i am happy to look into it - I tried a few patterns but was not very happy with those.

Does this PR introduce a user-facing change?

NONE

Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:


@k8s-ci-robot k8s-ci-robot added release-note-none Denotes a PR that doesn't merit a release note. kind/bug Categorizes issue or PR as related to a bug. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. area/apiserver labels Sep 10, 2021
@k8s-ci-robot k8s-ci-robot added sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/instrumentation Categorizes an issue or PR as relevant to SIG Instrumentation. and removed do-not-merge/needs-sig Indicates an issue or PR lacks a `sig/foo` label and requires one. labels Sep 10, 2021
@tkashem tkashem changed the title apiserver: decorate http.ResponseWriter correctly [WIP] apiserver: decorate http.ResponseWriter correctly Sep 10, 2021
@k8s-ci-robot k8s-ci-robot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 10, 2021
@tkashem tkashem force-pushed the response-writer-cleanup branch 2 times, most recently from 8c28ee4 to cb0df8c Compare September 10, 2021 21:19
@tkashem
Copy link
Contributor Author

tkashem commented Sep 10, 2021

@wojtek-t @sttts can you please look at it when you have some time

/assign @wojtek-t @sttts

@tkashem tkashem changed the title [WIP] apiserver: decorate http.ResponseWriter correctly apiserver: decorate http.ResponseWriter correctly Sep 10, 2021
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Sep 10, 2021
@tkashem
Copy link
Contributor Author

tkashem commented Sep 10, 2021

cc @MikeSpreitzer

@tkashem tkashem force-pushed the response-writer-cleanup branch 2 times, most recently from e878e95 to 10be4b9 Compare September 11, 2021 01:46
@tkashem
Copy link
Contributor Author

tkashem commented Sep 13, 2021

httplog.Unlogged is called in 3 more places, we will need to extend the decorator so that the handler logic can get to the original http.ResponseWriter object from the decorator, thus eliminating the strong coupling we have with http.Unlogged

@MikeSpreitzer
Copy link
Member

Yes, I think we should create and use a generic abstraction for this decoration. I tried drafting that, have a look at #104943 .

@tkashem
Copy link
Contributor Author

tkashem commented Sep 13, 2021

Yes, I think we should create and use a generic abstraction for this decoration. I tried drafting that, have a look at #104943 .

@MikeSpreitzer thanks, I initially tried a similar pattern - I will update the PR with a reusable abstraction.

Copy link
Member

@MikeSpreitzer MikeSpreitzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/LGTM
Thanks!

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 1, 2021
@tkashem
Copy link
Contributor Author

tkashem commented Oct 1, 2021

Needs an approval from kubelet, @sjenning please look at it when you have some time

/assign @sjenning

klog.InfoDepth(1, fmt.Sprintf("Unable to convert %+v into http.Flusher", rl.w))
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is this removal fine?

@k8s-ci-robot k8s-ci-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 4, 2021
@sttts
Copy link
Contributor

sttts commented Oct 4, 2021

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 4, 2021
@tkashem
Copy link
Contributor Author

tkashem commented Oct 4, 2021

/assign @mrunalp

@mrunalp
Copy link
Contributor

mrunalp commented Oct 5, 2021

/approve

@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: MikeSpreitzer, mrunalp, sttts, tkashem

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Oct 5, 2021
@sttts
Copy link
Contributor

sttts commented Oct 5, 2021

/retest

@k8s-ci-robot k8s-ci-robot merged commit efa9029 into kubernetes:master Oct 5, 2021
SIG Node PR Triage automation moved this from Needs Reviewer to Done Oct 5, 2021
@k8s-ci-robot k8s-ci-robot added this to the v1.23 milestone Oct 5, 2021
@leoryu
Copy link

leoryu commented Nov 9, 2021

Will this pr be merged in v1.22.x?

@tkashem
Copy link
Contributor Author

tkashem commented Nov 9, 2021

Will this pr be merged in v1.22.x?

It's not small enough or critical enough to be back ported to 1.22.x. Is there any particular reason you want it in 1.22.x?

@leoryu
Copy link

leoryu commented Nov 10, 2021

Will this pr be merged in v1.22.x?

It's not small enough or critical enough to be back ported to 1.22.x. Is there any particular reason you want it in 1.22.x?

My project is using Aggregation API to expand k8s, and it imports apiserver. After I update apiserver version to v0.22.3, I got err logs which is mentioned in this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. area/apiserver area/kubelet cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/bug Categorizes issue or PR as related to a bug. lgtm "Looks good to me", indicates that a PR is ready to be merged. priority/important-soon Must be staffed and worked on either currently, or very soon, ideally in time for the next release. release-note-none Denotes a PR that doesn't merit a release note. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/instrumentation Categorizes an issue or PR as relevant to SIG Instrumentation. sig/node Categorizes an issue or PR as relevant to SIG Node. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. triage/accepted Indicates an issue or PR is ready to be actively worked on.
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

None yet

10 participants