-
Notifications
You must be signed in to change notification settings - Fork 351
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
Add flag for Oauth request timeout #707
Conversation
Thanks for the improvements. I added a couple of comments. |
cmd/skipper/main.go
Outdated
oauthCredentialsDirUsage = "directory where oauth credentials are stored: client.json and user.json" | ||
oauthScopeUsage = "the whitespace separated list of oauth scopes" | ||
oauth2TokeninfoURLUsage = "sets the default tokeninfo URL to query information about an incoming OAuth2 token in oauth2Tokeninfo filters" | ||
oauth2TokeninfoTimeoutUsage = "sets the default tokeninfo request timeout duration" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's add some info what a value of 0 means - no timeout, stdlib default (how much?), etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it enough to add the timeout value of 1500ms from defaultOAuthTokeninfoTimeout
?
cmd/skipper/main.go
Outdated
oauthCredentialsDirUsage = "directory where oauth credentials are stored: client.json and user.json" | ||
oauthScopeUsage = "the whitespace separated list of oauth scopes" | ||
oauth2TokeninfoURLUsage = "sets the default tokeninfo URL to query information about an incoming OAuth2 token in oauth2Tokeninfo filters" | ||
defaultOAuthTokeninfoTimeout = 100 * time.Millisecond // Not sure if this makes sense |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems too short as a default to me. It should be more relaxed by default and be able to accommodate, let's say, the redirects the default client will follow - which count towards the timeout. I propose something more in the range of 1~2 sec as default. End users should customize that for their own use case. If your use case considers more appropriate 100ms, that's the value you should use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 and please move the defaults to the defaults section, thanks
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated this to 1500ms
skipper.go
Outdated
@@ -433,6 +433,9 @@ type Options struct { | |||
// OAuthTokeninfoURL sets the OAuthTokeninfoURL similar to https://godoc.org/golang.org/x/oauth2#Endpoint | |||
OAuthTokeninfoURL string | |||
|
|||
// OAuthTokeninfoTimeout sets timwout duration while calling oauth token service |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo s/timwout/timeout
filters/auth/oauth.go
Outdated
@@ -251,7 +261,9 @@ func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) | |||
return nil, filters.ErrInvalidFilterParameters | |||
} | |||
|
|||
ac, err := newAuthClient(s.tokeninfoURL) | |||
log.Info("Log the timeout ", s.tokenInfoTimeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove this log line
filters/auth/oauth.go
Outdated
rsp, err := http.DefaultClient.Do(req) | ||
var client *http.Client | ||
if timeout != 0 { | ||
client = &http.Client{Timeout: timeout} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will not failover DNS if someone will change the traffic to another loadbalancer for example.
We need to have the workaround as in the kubernetes dataclient or the proxy.
See https://github.com/zalando/skipper/blob/master/dataclients/kubernetes/kube.go#L310-L337.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@szuecs Is this still true since the introduction of Transport.IdleConnTimeout in go 1.7? I think that new setting which defaults to 90secs is good enough to mitigate that problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, right now it’s the same problem, but we have an open issue golang/go#23427
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@szuecs do I need to create the same Transport dialer in the auth filter?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addityasingh you don't need the same, but you need the for {}
loop below the transport to close idle connections to make the http client to do a DNS call to make DNS based failover happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the DNS problem should be fixed and the default as commented from @lmineiro and me cheanged.
@@ -655,10 +658,10 @@ func Run(o Options) error { | |||
} | |||
|
|||
if o.OAuthTokeninfoURL != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@szuecs I am not sure if I should check for the o.OAuthTokeninfoTimeout
field here, since we always will have a default. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No you should not check timeout here
readme.md
Outdated
@@ -100,15 +100,20 @@ logging. See the documentation at: | |||
|
|||
Getting the code with the test dependencies (`-t` switch): | |||
|
|||
1. Get skipper using `go get` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@aryszka Can you check if this makes sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@addityasingh have you checked the 'Working with the code' section below? The reason for this 'Installation' section is left more open, is that 1/ 'go get' actually installs the skipper command in GOPATH/bin, 2/ it leaves the choice between using the latest or the bound version of the dependencies to the user.
This doesn't mean I am not open to improve the readme, but I think it should be a discussion in a separate PR. Can you please move the changes to the readme into a new, separate PR?
filters/auth/oauth.go
Outdated
@@ -145,9 +149,35 @@ func intersect(left []string, right []string) bool { | |||
return false | |||
} | |||
|
|||
func createHttpClient(timeout time.Duration) *http.Client { | |||
var client *http.Client | |||
if timeout != 0 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@szuecs IMO, since we will always have a default value for the OAuthTokeninfoTimeout
, it doesn't make sense to have a default client and always use the one with timeout. Thoughts ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes use always timeouts
filters/auth/oauth.go
Outdated
|
||
client = &http.Client{Transport: transport} | ||
|
||
go func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to have a quit channel to not leak this goroutine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At which part of the lifecycle should the quit
channel be closed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@szuecs I did not find any lifecycle method in oauth
filter similar to Close()
method in the kubernetes ingress controller. I have added the quit
channel to return from the goroutine
but still not sure at which point to send to the quit
channel. I checked the Explicit Cancellation in go pipleines concurrency patterns but could not find the point where to trigger the quit channel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a sigterm handler and use quit channels right now. Please check the kubernetes dataclient or the client connection pool in proxy/proxy.go
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I checked the quit channel implementations in those 2 but the kubernetes/dataclients
and proxy/proxy.go
have Close()
methods, but a filter doesn't. Should I implement a custom dataclient with Close()
method for the oauth filter?
readme.md
Outdated
make deps | ||
make install | ||
``` | ||
2. Install all dependencies using `glide` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have a make task, 'deps', that installs the dependencies
readme.md
Outdated
```sh | ||
glide install | ||
``` | ||
3. To build the packages and skipper cli, run `make` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i'd prefer not using the term 'cli'.
filters/auth/oauth.go
Outdated
|
||
client = &http.Client{Transport: transport} | ||
|
||
go func() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i wonder if this is required. If it is required, then we should make it possible to return from this goroutine.
filters/auth/oauth.go
Outdated
@@ -159,7 +189,8 @@ func jsonGet(url *url.URL, auth string, doc interface{}) error { | |||
return err | |||
} | |||
|
|||
rsp, err := http.DefaultClient.Do(req) | |||
client := createHttpClient(timeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should not create a new client on every request.
cmd/skipper/main.go
Outdated
@@ -57,6 +57,9 @@ const ( | |||
defaultTLSHandshakeTimeoutBackend = 60 * time.Second | |||
defaultMaxIdleConnsBackend = 0 | |||
|
|||
// OAuth2: | |||
defaultOAuthTokeninfoTimeout = 1500 * time.Millisecond // Not sure if this makes sense |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It makes sense, but still a bit opinionated in my view 😄 . How about time.Minute?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have changed it to 2 * time.Second
u, err := url.Parse(baseURL) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &authClient{url: u}, nil | ||
|
||
quit := make(chan struct{}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to call close(quit)
, I would the following:
- add a Close() method to the filterspec
- call
close(spec.authClient.quit)
from this Close() method - for normal skipper operation do nothing, do not call close, because there's going to be only a single instance of the spec
- for tests, always call Close() on the spec
- document that in case someone uses filter spec as a library in a custom build, they need to call Close() if they may create an arbitrary number of spec instances
@addityasingh @szuecs what do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sounds good to me
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach looks clean to me, since we wouldn't need to pass down quit
channel all the way down from skipper
instance creation. And since the Close()
is on filterspec
, we wouldn't need to bother about the leaking goroutine as well. @szuecs Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the FilterSpec
interface doesn't have the Close
method, this approach wouldn't work. Check this https://play.golang.org/p/LkpQRoatvvn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might be able to type cast it. Keep in mind if you need access from another package you have to export the type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It works in the playground, but not in the skipper test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see you found out. An interface can only be casted to, if you use the pointer IIRC.
filters/auth/oauth.go
Outdated
@@ -280,6 +314,13 @@ func (s *tokeninfoSpec) CreateFilter(args []interface{}) (filters.Filter, error) | |||
return f, nil | |||
} | |||
|
|||
// Close cleans-up the quit channel used for this spec | |||
func (s *tokeninfoSpec) Close() { | |||
if s.authClient.quit != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A spec can create filters.
I think you have to close it from the filter
, that's why you get a nil ptr panic.
See https://github.com/zalando/skipper/pull/707/files#diff-82513d1c973748c3c6b7fa0677fd3fd4L259, which shows that you store it in the filter and not in the spec.
👍 |
1 similar comment
👍 |
Thanks a lot for this PR! 😄 |
Close #633