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

controller: resolve external services with public DNS #360

Closed
wants to merge 2 commits into from

Conversation

seanmonstar
Copy link
Contributor

@seanmonstar seanmonstar commented Feb 14, 2018

This is a pretty basic stab at providing DNS support for external services (see #155). Notably:

  • This doesn't use any DNS library at the moment, just net.LookupHost. This means it was much easier to implement, but that it doesn't observe TTLs, and so the interval it checks for updates is rather arbitrary.
  • I tried to follow what I saw in the Endpoints stuff to keep only 1 querying thing per host, even if multiple proxies are all asking for it.
  • Hopefully the informer.run() part is sufficiently separate that we could later add in usage of dns or coredns or something.

Closes #155

@olix0r olix0r added the review/ready Issue has a reviewable PR label Feb 14, 2018
Copy link
Member

@adleong adleong left a comment

Choose a reason for hiding this comment

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

Looks reasonable to me so far

informer.mutex.Lock()
defer informer.mutex.Unlock()

for l in range(informer.listeners) {
Copy link
Member

Choose a reason for hiding this comment

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

if I remember correctly, this might be giving you the index instead of the item....


for l in range(informer.listeners) {
if l == listener {
//todo
Copy link
Member

Choose a reason for hiding this comment

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

something to watch out for: you probably want to stop the informer and remove it from the map if the list of listeners becomes empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's what the stopCh is meant as well. In Unsubscribe, I expect that if the list has become empty, to shutdown that informer. Still looking up super basic things like removing an element from an array :D

@seanmonstar seanmonstar force-pushed the controller-public-dns branch 12 times, most recently from 39b7789 to 36babcd Compare February 15, 2018 02:25
@seanmonstar
Copy link
Contributor Author

seanmonstar commented Feb 15, 2018

I wanted to start trying to write tests for this file, but have hit some snags in how best to do that. I'd love to create some testListener, and be able to spell out a bunch of arrays that would be called on informer.update(addrs), and that with each of those calls, be able to test testListener.Update(add, remove) was called with the right arrays. Can I easily smash common.TcpAddresss into strings and compare for testing purposes?

Signed-off-by: Sean McArthur <sean@seanmonstar.com>
@adleong
Copy link
Member

adleong commented Feb 15, 2018

in util/util.go there is func AddressToString(addr *pb.TcpAddress) string {
Does that help?

@seanmonstar seanmonstar force-pushed the controller-public-dns branch 4 times, most recently from 24ffc5b to 79aa55b Compare February 16, 2018 22:56
Signed-off-by: Sean McArthur <sean@seanmonstar.com>
@seanmonstar
Copy link
Contributor Author

Woo, unit tests pass, and theres a relcon test passing with this also.

So, uh, needs a review. Also, there's some implementation details that still have questions:

  • The port is just always assumed to be 80. Is there a time when that's incorrect?
  • This picks an arbitrary interval of 10 seconds. Should it be more? Less?

Copy link
Member

@adleong adleong left a comment

Choose a reason for hiding this comment

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

This looks really great. A few comments below.

@@ -0,0 +1,186 @@
package destination
Copy link
Member

Choose a reason for hiding this comment

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

I'd consider naming this file dns_watcher.go since the contents of the file make no reference to the term egress

}

func (i* informer) update(addrs []string) {
// diff with current set, send any updates
Copy link
Member

Choose a reason for hiding this comment

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

There is a diff function in endpoints.go that could be factored out into util.go and then re-used here.

}
additions = append(additions, common.TcpAddress{
Ip: ip,
Port: 80, //magical
Copy link
Member

Choose a reason for hiding this comment

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

I believe the host can optionally include a port. We should split on : and use the specified port if it exists. Otherwise, we should use the default port for the protocol. This probably requires a change to the destination protobuf to start sending the protocol.

Copy link
Member

Choose a reason for hiding this comment

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

Alternatively, we can punt on this and default to 80 for now, but this will not work for HTTPS egress calls.

@klingerf klingerf added this to the Conduit 0.3 milestone Feb 19, 2018
@briansmith
Copy link
Contributor

See #392.

@olix0r
Copy link
Member

olix0r commented Feb 20, 2018

@briansmith thanks for pointing this out. that is, indeed, an interesting wrinkle.

If I understand correctly, the following would work for us, at least for this release:

  1. proxy tries to resolve all names through the controller. the controller does NOT attempt DNS resolution.
  2. if the controller cannot resolve the name through the kubernetes API, the proxy simply uses the original dst addr, which should be already resolved through DNS by the application

Would this be correct behavior for 0.3?

@seanmonstar If so, I imagine this isn't all that hard to implement in the next day. (I can help on the proxy work, if needed).

@adleong
Copy link
Member

adleong commented Feb 20, 2018 via email

@briansmith
Copy link
Contributor

proxy to tries to resolve all names through the controller. the controller does NOT attempt to DNS resolution.
if the controller cannot resolve the name through the kubernetes API, the proxy simply uses the original dst addr, which should be already resolved through DNS by the application

The controller would have to return a distinct indicator that means "don't rely on me for this name." I don't know if it currently does that; it might need to be modified to do so.

Would this be correct behavior for 0.3?

It would be more correct than the current behavior and safer than what's proposed in this PR.

It wouldn't be completely correct because we'd still resolve metadata as metadata.$namespace.svc.$zone. However, resolving that requires doing a DNS lookup for metadata specifically and I don't think that's something that can be implemented well in a day.

I suggest a slight variation of your suggested implementation, by making the decision in the proxy instead of in the Destination service: If default_zone.is_none() and the name is in the form $a.$b.svc, or if !default_zone.is_none() and the name is in the form $a.$b.svc.$default_zone, for some a and some b, then use the Destination service. Otherwise, use the IP given. That way, we don't even ping the Destination service for such names.

Either your way or this more efficient way would allow external services to work.

Regardless, in 0.4 we'll need to properly handle names like metadata and localhost by actually using DNS and other configuration information to decide whether we should append $namespace.svc and/or $default_zone to the name and resolve it through the Destination service.

@briansmith
Copy link
Contributor

briansmith commented Feb 20, 2018

Should the proxy cache that a particular authority is not resolvable by the
destination service? For how long?

Or should the destination API be expanded to include a state in the
response stream which indicates that the destination service could not
resolve the authority?

I think this is all addressed by my suggestion above in #360 (comment).

@seanmonstar
Copy link
Contributor Author

Closing in favor of #397

@seanmonstar seanmonstar removed the review/ready Issue has a reviewable PR label Feb 20, 2018
@olix0r olix0r deleted the controller-public-dns branch April 30, 2018 23:20
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.

None yet

5 participants