-
Notifications
You must be signed in to change notification settings - Fork 5.6k
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
feat(inputs.http_listener_v2): Add unix socket mode #15764
Conversation
Thanks so much for the pull request! |
11bf51e
to
06bfdea
Compare
!signed-cla |
1 similar comment
!signed-cla |
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.
Thanks for your contribution @bazko1! I do have some smaller comments in the code, please take a look!
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.
Thanks @bazko1, the code looks really good. Just some more small comments...
if runtime.GOOS == "windows" && strings.HasPrefix(h.ServiceAddress, "unix://") { | ||
u = &url.URL{Scheme: "unix", Path: strings.TrimPrefix(h.ServiceAddress, "unix://")} | ||
} else { |
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 this really necessary? Parsing a unix URL should also work on Windows, shouldn't it?
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 it was problematic as sample address will look like unix://c:/Users/bazko1/temp/telegraf.socket
the url.Parse
starts to interpret the part after c:
as port and it results in an parse error.
You can check that in socket_listener
tests for windows are skipped with comment that the unixgram is not supported even though the unix socket is.
See: https://github.com/influxdata/telegraf/blob/master/plugins/inputs/socket_listener/socket_listener_test.go#L125-L128
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 think the correct way is to use unix:///C:/Users/bazko1/...
(i.e. three slashes) according to golang/go@844b625...
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 great I tested it and this works I will remove the windows case then.
func TestServiceAddressURL(t *testing.T) { | ||
unixSocket := filepath.FromSlash(os.TempDir() + "/test.sock") | ||
cases := []struct { | ||
serviceAddress, expectedAddress string | ||
expectedPort int | ||
shouldError bool | ||
}{ | ||
{":8080", "[::]:8080", 8080, false}, | ||
{"localhost:4123", "127.0.0.1:4123", 4123, false}, | ||
{"tcp://localhost:4321", "127.0.0.1:4321", 4321, false}, | ||
{"127.0.0.1:9443", "127.0.0.1:9443", 9443, false}, | ||
{"tcp://127.0.0.1:8443", "127.0.0.1:8443", 8443, false}, | ||
{"tcp://:8443", "[::]:8443", 8443, false}, | ||
// port not provided | ||
{"8.8.8.8", "", 0, true}, | ||
{"unix://" + unixSocket, unixSocket, 0, false}, | ||
// wrong protocol | ||
{"notexistent:///tmp/test.sock", "", 0, true}, | ||
} | ||
for _, c := range cases { | ||
listener, err := newTestHTTPListenerV2() | ||
require.NoError(t, err) | ||
listener.ServiceAddress = c.serviceAddress | ||
err = listener.Init() | ||
require.Equal(t, c.shouldError, err != nil, "Init returned wrong error result error is: %q", err) | ||
if c.shouldError { | ||
continue | ||
} | ||
acc := &testutil.Accumulator{} | ||
require.NoError(t, listener.Start(acc)) | ||
require.Equal(t, c.expectedAddress, listener.listener.Addr().String()) | ||
require.Equal(t, c.expectedPort, listener.Port) | ||
listener.Stop() | ||
} | ||
} | ||
|
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 test is problematic in CI environments. Imagine another test using the same port, if they by chance run at the same time one of them will fail and we get flaky tests. I would remove the test here or solely stick to the URL parsing part without actually "starting" the listener...
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 I agree such test might be problematic when running in parallel with others, but afaik they are run synchronously and there are also other tests that actually, start listening on port.
Anyway my logic should be tested enough without Start
as it is located in Init
function so I will remove the Start
method call.
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.
Well actually the Init
already calls the net.Listen
so that the port gets busy and we need to stop the internal dial.Listener
.
I think my changes are big enough especially with special windows case that having unit test for url parsing would be nice so please reconsider the idea of fully removing test, but if you insist I will remove.
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.
Well starting the listener in Init()
is also wrong, this should be done in Start()
... Anyhow, as you didn't introduce this, I don't blame you. ;-)
Regarding the tests: Yes, the tests in this file/package are executes sequentially, but all packages are executed in parallel (in the best case)... So if e.g. outputs.http
opens a server on this port we run into trouble... So it is acceptable to specify no port or port zero as this will allow the OS to choose a free port but the above will definitively bite us. Been there suffered that.
In any case, what you do is to test if url.Parse
works I think and I guess we should assume this works at least as long as this test will open a fixed port... So either change the test/code to not starting listen or remove the fixed ports.
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.
Okay lets make it right then I agree that the test can be actually removed especially when windows unix path works fine with url.Parse.
I will move the .Listen
part to Start
then.
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.
Done in 45792b5
Download PR build artifacts for linux_amd64.tar.gz, darwin_arm64.tar.gz, and windows_amd64.zip. 👍 This pull request doesn't change the Telegraf binary size 📦 Click here to get additional PR build artifactsArtifact URLs |
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.
Thanks for the nice PR @bazko1!
Summary
Following changes add two new options to
http_listener_v2
->network
defaulttcp
can beunix
which results inservice_address
interpreted as unix socket path that will be created and listened on by HTTP server.socket_mode
- for editing socket file permission settings, by default""
can be for example644
only available fornetwork: "unix"
Why:
I would like to have possibility of writing HTTP messages over unix socket to push messages to telegraf using libcurl based tools. This allows me to limit access to writing/reading messages with built in users/groups based mechanisms instead of trying to limit access via some firewall based options. More explanation can be found in linked issue.
Checklist
Related issues
resolves #15730
Considerations
For now I added single unit test for unix network case I could instead extend some of the existing ones to be something like:
I was thinking about directly using Socket from socket_listener but its
listener
field is made private and I decided to not change that thus some of the code is basically copied from its implementation for example the SocketMode part.