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

RPC and HTTP interfaces fully generically-sockified so Unix is supported... #587

Merged
merged 7 commits into from
Jan 15, 2015

Conversation

jefferai
Copy link
Member

@jefferai jefferai commented Jan 8, 2015

....

Client works for RPC; will honor CONSUL_RPC_ADDR. HTTP works via consul/api;
honors CONSUL_HTTP_ADDR.

The format of a Unix socket in configuration data is:
"unix://[/path/to/socket];[username or uid];[gid];[mode]"

Obviously, the user must have appropriate permissions to create the socket
file in the given path and assign the requested uid/gid. Also note that Go does
not support gid lookups from group name, so gid must be numeric. See
https://codereview.appspot.com/101310044

When connecting from the client, the format is just the first part of the
above line:
"unix://[/path/to/socket]"

This code is copyright 2014 Akamai Technologies, Inc. [email protected]

@armon
Copy link
Member

armon commented Jan 8, 2015

Awesome! Do you think you could add appropriate tests for all these changes? Where necessary we can guard them with a runtime.GOOS to make sure we don't run on Windows.

@jefferai
Copy link
Member Author

jefferai commented Jan 8, 2015

In past conversation with you on IRC you indicated that, since in theory you'd want to run all tests with these parameters set, it would be acceptable to test the changes by setting them and then re-running the normal test suite. This implies a more manual process, but otherwise I'm not sure what the best way to go about it is (maybe munging the makefile that handles "make test"?)

If you're thinking of more basic individual tests to simply ensure that Consul can be spun up and a client can successfully connect when using Unix sockets, then yes, I can do that, except that as we've previously discussed since nearly all unit tests (on master) fail for me right now I wouldn't have a good baseline for determining whether these would be an issue.

@armon
Copy link
Member

armon commented Jan 8, 2015

We strive for as high of test coverage as possible, and I think there are lots of testable areas with this patch. All of the tests are currently passing for us, and I believe @ryanuber changed the tests that were failing for you so that they should be passing more reliably as well.

@jefferai
Copy link
Member Author

jefferai commented Jan 8, 2015

Sadly, this is not the case. Half of the tests still fail on current master (as of a couple of minutes ago). See https://gist.github.com/jefferai/b9f366330f1fbbfbe3e8 for a log.

@jefferai
Copy link
Member Author

jefferai commented Jan 8, 2015

FWIW, machine is Ubuntu 14.04 running in VirtualBox on an early 2013 MacBook Pro. Go 1.3.3. Shouldn't be anything super weird.

@jefferai
Copy link
Member Author

jefferai commented Jan 8, 2015

More debugging: after setting GOMAXPROCS=2 and re-running "make test" all tests passed. When I ran "make test" again afterwards, some of the tests failed again.

@jefferai
Copy link
Member Author

jefferai commented Jan 8, 2015

OK, so for right now I'm taking the approach of "my code shouldn't fail any harder than master does" and with that, I found an issue.

In toHTTP in api.go there is a urlRaw parameter. You can see that I changed this in my commits in favor of using the path and later explicitly setting the scheme and host in the request. The reason is that urlRaw will always return a string with http:// even when it should be returning just a path. I went down a rabbit hole of Go code and basically this is a hardcoded assumption that is made. I believe, but can check, that simply converting "http" to "unix" when passing into http.NewRequest did not work. By making the change that I did, various commands I tested (such as "consul watch") were able to connect and pull data from the http interface over a Unix socket.

Unfortunately this breaks the API unit tests rather badly. I'll be digging into this and looking for an alternate solution, but any insights would be helpful.

@jefferai
Copy link
Member Author

Just as an update, I was unable to work on this over the weekend due to being quite sick, but I've knocked down one of the unit test failures and updated the branch accordingly.

@jefferai
Copy link
Member Author

As of this point, as far as I can tell, this branch does not fail any more than the master branch. The master branch still fails (not deterministically) in api/ and consul/, which makes it hard to ensure the remaining failures are just due to race conditions.

@ryanuber
Copy link
Member

@jefferai The tests passing as often as master is a good sign. I do still think we need some general testing around some of the functions that are going in here though. For example, populateUnixSocket and adjustUnixSocketPermissions seem like fairly generic functions which we could have some unit testing for individually. We could use the runtime to figure out if we are running on Linux/UNIX, and skip if we are not with t.SkipNow().

@jefferai
Copy link
Member Author

@ryanuber Sure. I haven't had time to get to new tests yet, just fixing the one regression I had against old tests and running over and over to determine what's likely to be a problem and what's just test brokenness. New tests are next.

@ryanuber
Copy link
Member

@jefferai sounds good, just let us know when you think its ready and we'll take another look!

@jefferai
Copy link
Member Author

@ryanuber @armon in agent.go, in Create, a configuration is passed in; this configuration is then ignored by the agent.setupServer and agent.setupClient calls, which means my adjustments to the config to have it listen on Unix sockets have no effect (on either the server or client).

I'd like to change setupServer and setupClient to honor config.Addresses if set to something other than the defaults. This feels slightly intrusive, but I don't see any other way. Comments?

@armon
Copy link
Member

armon commented Jan 13, 2015

@jefferai Not sure why they would be impacted? Since the HTTP listener is in the agent level and not at the Consul level.

@jefferai
Copy link
Member Author

OK, I think the issue is that I didn't notice that the config was being translated from an agent.Config to consul.Config. I will try to dig into the code more but I'm a bit unclear as to where the separations between "server" and "client" are. For instance, the RPC socket provided by the running Consul instance is provided by the agent, and when I start Consul normally it honors what I choose (Unix/TCP). However, in a test that calls makeAgent with conf.Server set to true, it starts the "server" part of Consul but does not actually pay any attention to the RPC bits of the config. In TestAgent_RPCPing, which seemed like a logical place to base a Unix socket test off of, it seems like it's actually just talking to the "server" on the backend and not through the normal RPC interface.

I'll keep digging and trying to find a better place, but any guidance/pushes in the right direction would be useful.

Jeff Mitchell added 5 commits January 14, 2015 19:31
…ted.

Client works for RPC; will honor CONSUL_RPC_ADDR. HTTP works via consul/api;
honors CONSUL_HTTP_ADDR.

The format of a Unix socket in configuration data is:
"unix://[/path/to/socket];[username or uid];[gid];[mode]"

Obviously, the user must have appropriate permissions to create the socket
file in the given path and assign the requested uid/gid. Also note that Go does
not support gid lookups from group name, so gid must be numeric. See
https://codereview.appspot.com/101310044

When connecting from the client, the format is just the first part of the
above line:
"unix://[/path/to/socket]"

This code is copyright 2014 Akamai Technologies, Inc. <[email protected]>
… major function, and basic socket listening tests.

This code is copyright 2014 Akamai Technologies, Inc. <[email protected]>
…nctions to be cleaner. Start of runtime tests.

This code is copyright 2014 Akamai Technologies, Inc. <[email protected]>
…ry to not make assumptions about the listening socket; all RPC tests still pass. Still to do: Unix socket HTTP test.

This code is copyright 2014 Akamai Technologies, Inc. <[email protected]>
also required making some hardcoded values into more generic
functionality, which is generally a good thing. I verified that each
test function that I modified still passed.:

This code is copyright 2014 Akamai Technologies, Inc. <[email protected]>
@jefferai
Copy link
Member Author

@armon @ryanuber I believe this is now ready for review. Also, let me know if you'd like me to squash these into either one or two (code in one, tests in the other) commits, or leave as-is.

@ryanuber
Copy link
Member

@jefferai I'll take a look at this in just a bit, thanks!


req.URL.Host = r.url.Host
req.URL.Scheme = r.url.Scheme
req.Host = r.url.Host

// Setup auth
if err == nil && r.config.HttpAuth != nil {
Copy link
Member

Choose a reason for hiding this comment

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

We can probably remove the nil part of this check now that we have already checked it / returned if non-nil above.

@ryanuber
Copy link
Member

@jefferai I went through and added some comments about various things. Overall looking really good!

I am wondering about the format of the UNIX socket URI since it includes ;[user];[group];[mode] at the end. I might be missing something here. Is this a typical UNIX socket path, or are we inventing some syntax here? Maybe you could elaborate on why we are taking that path here.

items pointed out in the code review

This code is copyright 2014 Akamai Technologies, Inc. <[email protected]>
@jefferai
Copy link
Member Author

@ryanuber OK, I've gone through the issues above.

A typical UNIX socket path is unix:///path/to/socket/file. The user/group/mode are necessary parts of the specification though. The reason it's in that form is compatibility with the current configuration, which is a string. Most other applications have the owner/group/mode as separate configuration items.

The only way to do it the "normal" way would be to change the configuration file format, or at a minimum add several more values for each of the rpc and http addresses, either in a sub-object or as extra optional keys.

When I proposed this on the mailing list (https://groups.google.com/forum/#!topic/consul-tool/9m4iKt6PTgE) I got no indication from either armon or other users that this method was unsatisfactory, and it retains compatibility. I'm fine with a change to the configuration syntax instead, but that would require a separate discussion with more parties involved, I think.

@ryanuber
Copy link
Member

@jefferai I've read through the comments, thanks for elaborating on the implementation. I'm going to merge this in and update a few minor stylistic things, but I think the functionality is there. I'll link you to the changeset afterwards. Thanks for doing this! 👍

ryanuber added a commit that referenced this pull request Jan 15, 2015
RPC and HTTP interfaces fully generically-sockified so Unix is supported...
@ryanuber ryanuber merged commit 36f9924 into hashicorp:master Jan 15, 2015
@jefferai jefferai deleted the unixlisten branch January 16, 2015 14:53
@ryanuber
Copy link
Member

@jefferai I pushed #612 as a follow-on to this PR. See the description there for details on what changed and why. Again, thanks for tackling this, we appreciate it!

@ryanuber ryanuber mentioned this pull request Jan 21, 2015
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