-
-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Safe check rlimit syscall and refactoring #3801
Conversation
Ok, few things:
EDIT: I see that freebsd code is in sync, but it would be best to DRY it up as the logic got a bit more complex. It should be safe to use uint64 in general and just have helper methods that would execute those calls (and case it to int64 in case of freebsd). |
cmd/ipfs/runmain_test.go
Outdated
) | ||
|
||
// this abuses go so much that I felt dirty writing this code | ||
// but it is the only way to do it without writing custom compiler that would | ||
// be a clone of go-build with go-test | ||
func TestRunMain(t *testing.T) { | ||
func (m mainRun) TestRunMain(c *gc.C) { |
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.
Not sure if it will fly with coverage collection code. We will see when dependency thing is fixed.
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 change is breaking coverage collection func TestRunMain(t *testing.T)
is run when testing starts and it takes over the testing process. In this case it just executes ipfs process not the tests. It is guided by the build flag of this file.
The ulimit raising is tested in sharness, so the unit tests are not needed 100% but useful nevertheless. If it causes problems with coverage collection we can move the ulimit raising to separate package and test it alone. |
I'm open to any good solution. From my standpoint of view I think every file should contains a *_test.go. It's kind of scary that the project is missing so much unit testing. |
Yes we are constantly trying to push for more unit testing. We have also quite a big sharness testing suite. Checkout |
cmd/ipfs/main_test.go
Outdated
"github.com/ipfs/go-ipfs/commands" | ||
|
||
gc "gopkg.in/check.v1" |
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 dependency has to be rewritten, please run gx-go rewrite
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.
Fixed !
This needs to be broken up into logical atomic changesets |
cmd/ipfs/ulimit_freebsd.go
Outdated
return fmt.Errorf("error setting ulimit without hard limit: %s", err) | ||
} | ||
|
||
log.Infof("Successfully raised file descriptor limit to %d.\n", maxFds) |
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.
Info is not default on channel so it won't be visible. We want this message to be visible during daemon start, that is the reason it was using fmt.Printf
before.
Sorry to party-poop too, but there has been previous proposals to bring-in testing helper libraries and so far they have not moved forward. This would effectively introduce testing deps. See #3498 |
cmd/ipfs/main_test.go
Outdated
if !isClientError(&commands.Error{Code: commands.ErrClient}) { | ||
t.Errorf("misidentified pointer") | ||
} | ||
func (m mainRun) TestIsCientErr(c *gc.C) { |
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 new way of testing this is not shorter, cleaner, or easier to understand. I'm not on board with this.
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 disagree completely.
I think more mantainers of this project should take a look and give more feedback.
The c.Assert besides of line number and fail message it contains these lines as well "expected: value, obtained: value". which it gives us way more information on "what went wrong". If you want I can use testify to write these tests.
But keep relaying and using the testing pkg from std lib it's not good idea, in the long run. (in my opinion).
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 are free to have your opinions, but to convince me that this is worthwhile and not just some random contributor trying to push their opinions on a project they just found you'll have to do better convincing me of this.
You need to choose better examples to prove your point, because this specific one is not helped by shoving a test lib into it. "expected: true, got: false" Is quite clear from reading the existing test and the debuggability is not helped by adding in the extra cognitive overhead of this framework.
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 to agree with @whyrusleeping on this, in this case misidentified value
is much more descriptive than expected: true, got: false
.
Also replacing the c.Assert(ok, gc.Equals, true)
with
if !ok {
t.Error("misidentified value")
}
Is easier and faster to type, conveys more information and everybody knowns what is going on.
There is no value added of c.Assert
in this part of code.
@whyrusleeping @Kubuxu now it's ok? |
cmd/ipfs/init.go
Outdated
@@ -9,6 +9,7 @@ import ( | |||
"path" | |||
|
|||
context "context" | |||
|
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.
while we're modifying this, lets go ahead and drop the named import (holdover from "golang.org/x/net/context"
) and move it up with the rest of the stdlib
cmd/ipfs/ulimit.go
Outdated
@@ -5,15 +5,25 @@ import ( | |||
"strconv" | |||
) | |||
|
|||
var ipfsFileDescNum = uint64(1024) | |||
// maxFds is the maximum number of file descriptors that the go ipfs |
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.
s/the go ipfs/go-ipfs/
cmd/ipfs/ulimit.go
Outdated
@@ -5,15 +5,25 @@ import ( | |||
"strconv" | |||
) | |||
|
|||
var ipfsFileDescNum = uint64(1024) | |||
// maxFds is the maximum number of file descriptors that the go ipfs | |||
// can use. Default values is 1024. This can be overwritten by the |
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.
s/Default values/The default value/
cmd/ipfs/ulimit.go
Outdated
} | ||
|
||
// assign the |
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.
useless comment
cmd/ipfs/ulimit_freebsd.go
Outdated
// in the range from 0 up to the hard limit | ||
if err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil { | ||
// the process does not have permission so we should only set the soft value | ||
if err == syscall.EPERM { |
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.
reverse the logic here: if err != syscall.EPERM { ...
cmd/ipfs/ulimit_test.go
Outdated
t.Logf("Testing file descriptor invalidity") | ||
var err error | ||
if err = os.Unsetenv("IPFS_FD_MAX"); err != nil { | ||
t.Errorf("Cannot unset the IPFS_FD_MAX env variable") |
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.
Use fatal for these, the rest of the test is invalid if this assertion (or the others) fails.
cmd/ipfs/ulimit_test.go
Outdated
t.Errorf("Managefd should return an error") | ||
} | ||
|
||
if err != 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.
else if
cmd/ipfs/ulimit_windows.go
Outdated
|
||
package main | ||
|
||
// managefd checks for the maximum number of file descriptors that the system supports |
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 comment is incorrect.
cmd/ipfs/ulimit_unix.go
Outdated
// the hard limit acts as a ceiling for the soft limit | ||
// an unprivileged process may only set its soft limit to a value | ||
// in the range from 0 up to the hard limit | ||
if err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != 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.
This logic is nearly identical to the other freebsd logic. I'm sure we can DRY it up a bit
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 few comments, lets try to DRY up the limit setting code so we don't mess up changes to it later
cmd/ipfs/ulimit_test.go
Outdated
|
||
rlimit := syscall.Rlimit{} | ||
if err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlimit); err != nil { | ||
t.Errorf("Cannot get the file descriptor count") |
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.
Fatal
here too as rest is useless if this fails
I would suggest pulling the fd managing code to separate package (it has nothing to do with ipfs main process itself) and also switching from directly referencing functions under build tag to using Example how it could be done: // hasFDManage is used to determine if we should run tests
// (if we have generic, platform antagonistic test (open a lot of empty files)).
var hasFDManage bool = false
var manageFDs func() error = func() { return nil }
func ManageFDs() error {
return manageFDs()
} Then file that builds on unix: func init() {
hasFDManage = true
manageFDs = unixManageFDs()
}
func unixManageFDs() Then as freebsd is special, one file for non freebsd unix: // +build unix,!freebsd
func getFDlimits...
func setFDlimits... and separate one for freebsd: // +build freebsd
func getFDlimits...
func setFDlimits... |
cmd/ipfs/util/ulimit.go
Outdated
return | ||
} | ||
|
||
// assign the |
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.
whats this comment here for?
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.
Sorry, I will delete it.
Why not use the runtime.GOOS ? |
@hoenirvili I'm assuming youre asking why use build tags over a switch on |
Can you please take a look one more time at the code? What changes should I make? |
@hoenirvili Something like this: https://gist.github.com/whyrusleeping/8c3bae34723f209665480f39d2163848 Let me know if i should be more detailed |
cmd/ipfs/util/ulimit.go
Outdated
"os" | ||
"strconv" | ||
|
||
"github.com/prometheus/common/log" |
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 is the wrong logging package
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.
Import logging "github.com/ipfs/go-logging"
and then do a global var log = logging.Logger("ulimit")
The reasoning for the construction in the linked gist is to ensure that when trying this for new unsupported platforms, one: it doesnt fail to build because we forgot a special file with its particular build tags, and two: its much easier to implement support on new systems, just by implementing the getFDLimit and setFDLimit functions on the new platform. |
Sorry for taking so much time... I was very busy these couple of weeks. I've updated the patch, you can take a look and tell what improvements I can add further. |
What about now ? |
@hoenirvili Hey, I'll take another look at this tomorrow. Sorry for taking so long to get back to you here, i've been out for a week |
@whyrusleeping 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.
See the comments and also CodeClimate issue.
cmd/ipfs/util/ulimit.go
Outdated
// maxFds is the maximum number of file descriptors that go-ipfs | ||
// can use. The default value is 1024. This can be overwritten by the | ||
// IPFS_FD_MAX env variable | ||
var maxFds = uint64(1024) |
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.
Master has increased it to 2048.
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.
Fixed
cmd/ipfs/util/ulimit.go
Outdated
} | ||
} | ||
|
||
func ManageFdLimit() (err error) { |
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.
remove the naming of the return value here
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.
Fixed
cmd/ipfs/util/ulimit.go
Outdated
|
||
func ManageFdLimit() (err error) { | ||
if !supportsFDManagement { | ||
return |
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.
and do explicit nil or error return here
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.
Fixed
// the hard limit acts as a ceiling for the soft limit | ||
// an unprivileged process may only set it's soft limit to a | ||
// alue in the range from 0 up to the hard limit | ||
if err = setLimit(max, max); err != 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.
I am not sure this is needed. Just setting it to max, hard should be ok (and before checking if max is < than hard).
// set the soft value | ||
if max > hard { | ||
return errors.New( | ||
"cannot set rlimit, IPFS_FD_MAX is larger than the hard limit", |
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.
That might be not true, the builtin default rise might be larger than the hard limit
cmd/ipfs/util/ulimit.go
Outdated
"strconv" | ||
"syscall" | ||
|
||
logging "github.com/ipfs/go-log" |
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.
run gx-go rw
to fix it and allow tests to run
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.
Fixed
Sharness test checking for the message is failing. |
@Kubuxu Yes, I know but, I don't have experience with the sharness framework to modify the code in order to make the sharness test logic pass. Can you provide some advice on this topic? |
Hey @hoenirvili! Sorry for taking nearly a month to get back to you here. I pulled your branch down and figured out how to get the tests passing, here was my diff:
I also removed the file cmd/ipfs/ulimit.go since it is no longer needed |
@whyrusleeping changed. |
The soft limit is the value that the kernel enforces for the corresponding resource The hard limit acts as a ceiling for the soft limit an unprivileged process may only set its soft limit to a value in the range from 0 up to the hard limit. So in order to make the change in fds count without any error we should inform the user to make the process have CAP_SYS_RESOURCE capability in order to set the hard limit. License: MIT Signed-off-by: hoenirvili <[email protected]>
@whyrusleeping I just rebased |
@kevina thanks! @hoenirvili Thanks for the changes! Sorry about the delay, i'm working on getting through the mountain of PRs now. |
This PR contains: