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

[balancer] Don't force 80 to be the default if port is not provided #662

Merged
merged 4 commits into from
Apr 23, 2018

Conversation

mayorova
Copy link
Contributor

An alternative (and a more generic) way to solve the issue with the upstream policy: #647

Copy link
Contributor

@mikz mikz left a comment

Choose a reason for hiding this comment

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

Looking pretty good but it might need a bit of reorganization to cleanup the API. Good job !

CHANGELOG.md Outdated

### Changed

- Changed the way `resty.balancer` sets the default port, in case no port is specified for the upstream [PR #662](https://github.com/3scale/apicast/pull/662)
Copy link
Contributor

Choose a reason for hiding this comment

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

The changelog should say how and not what. So it should say it uses default port for the scheme.

@@ -30,7 +30,7 @@ local function new_peer(server, port)

return {
address,
tonumber(server.port or port or 80, 10)
tonumber(server.port or port, 10) or nil
Copy link
Contributor

Choose a reason for hiding this comment

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

No need for or nil

@@ -87,7 +87,7 @@ function _M.select_peer(self, peers)
return peer
end

function _M.set_peer(self, peers)
function _M.set_peer(self, peers, default_port)
local balancer = self.balancer
Copy link
Contributor

Choose a reason for hiding this comment

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

I’m not really convinced by the API.

Shouldn’t this function extract the default port?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Extract from where? :)

Copy link
Contributor

Choose a reason for hiding this comment

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

I think the best approach is to not use set_peer but rather select_peer + balancer.set_current_peer.

function _M.call(_, _, balancer)
balancer = balancer or _M.default_balancer
local host = ngx.var.proxy_host -- NYI: return to lower frame
local peers = balancer:peers(ngx.ctx[host])

local peer, err = balancer:set_peer(peers)
local port = get_default_port(ngx.var.proxy_pass)
Copy link
Contributor

Choose a reason for hiding this comment

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

I checked the nginx docs and looks like there is no built in variable to get scheme of the upstream server. Pity.
We should evaluate this only when the port is missing.

ngx.log(ngx.ERR, "failed to set current backend peer: ", err)
ngx.exit(ngx.status)
ngx.log(ngx.ERR, 'could not select peer: ', err)
exit_service_unavailable()
Copy link
Contributor

Choose a reason for hiding this comment

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

Better to signal it returns by doing return exit_service_unavailable() as recommended in the ngx.exit documentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I thought about it, but for some reason didn't put it. Will fix, thanks.

@mayorova mayorova force-pushed the default-port-for-upstream branch 2 times, most recently from 9e6cbe8 to a121fdf Compare April 4, 2018 06:24
Copy link
Contributor

@mikz mikz left a comment

Choose a reason for hiding this comment

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

👍

assert.spy(b.balancer.set_current_peer).was.called_with('127.0.0.2', 443)
end)
end)
end)
Copy link
Contributor

Choose a reason for hiding this comment

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

👀

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mm? :)

Copy link
Contributor

Choose a reason for hiding this comment

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

Nitpick: There's no newline at the end of the file.

local function get_default_port(upstream_url)
local url = resty_url.split(upstream_url) or empty
local scheme = url[1] or 'http'
return resty_url.default_port(scheme)
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to clarify, I think this only works with http and https: https://github.com/3scale/lua-resty-url#default_port

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right. Do you think it needs to be documented somewhere in this PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

Well spotted. I think it does not need to be documented (because we don't really support other protocols), but handled with a proper error message.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess then we can just leave it to that and just have a test to verify what it does when no port is provided.

Copy link
Contributor

Choose a reason for hiding this comment

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

If the balancer has a default and would return an error then there is no need to handle this case by us. And to verify that it works it is necessary to have a test. IMO it is better to have a test to see how it works than code to maintain.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've tried it, but turns out that:

  1. setting unsupported://example.com in the upstream policy config will fail here, because trying to parse a URL with unsupported scheme returns a nil.

  2. If I force upstream to push the invalid URL forward, APIcast throws an error:
    [error] 4373#0: *5 invalid URL prefix in "unsupported://upstream"

So, I am not sure how I can test it in either integration or unit test :/

@mikz @davidor thoughts?

Copy link
Contributor

Choose a reason for hiding this comment

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

@mayorova fail how? throw an exception? then it should be handled and we can test it is being handled by asserting an entry in the error log and some status.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@mikz 1. will throw an exception...
My point is – I don't see a way to test how balancer will act if the incorrect scheme is passed, because the execution will not even reach the balancer point, and will fail before that.
Or should I add a failing test for the upstream policy instead? (I would consider it as a different PR - handle exception in upstream policy and add test)

Copy link
Contributor

Choose a reason for hiding this comment

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

port = get_default_port(ngx.var.proxy_pass)
end

local _
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks strange.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah :/
before it was an ok, but codeclimate complained about an unused variable.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe err = balancer.balancer.set_current_peer(address, port)[2] ?... because we don't care about the ok value (or maybe we should)

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes we should care about ok.

local peer, err = balancer:set_peer(peers)
local peer, err = balancer:select_peer(peers)

local address, port
Copy link
Contributor

Choose a reason for hiding this comment

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

You can move this below the if.

end

if not port then
port = get_default_port(ngx.var.proxy_pass)
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this can return nil because of the limitations of resty_url.default_port that I pointed out in another comment.
Not sure if that would be a problem later on.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I guess it can, but not sure what we should do in this case.
Defaulting to some specific port doesn't sound too good either. I guess here we can accept that apicast will return a 5xx error.

Copy link
Contributor

Choose a reason for hiding this comment

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

As pointed in #662 (comment) the port will be set to 0. So we just need to to have a test to see what it really does and we might not need to handle anything.

@davidor
Copy link
Contributor

davidor commented Apr 4, 2018

@mayorova The PR looks good 👍
My comments are about small issues.

@mayorova mayorova force-pushed the default-port-for-upstream branch 2 times, most recently from 58adfba to 55e4339 Compare April 4, 2018 16:42
Copy link
Contributor

@mikz mikz left a comment

Choose a reason for hiding this comment

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

Great job 👍 🥇

@davidor davidor merged commit ce0045d into master Apr 23, 2018
@davidor davidor deleted the default-port-for-upstream branch April 23, 2018 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants