-
-
Notifications
You must be signed in to change notification settings - Fork 5.8k
Refactor git.Command.Run*
, introduce RunWithContextString
and RunWithContextBytes
#19266
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
Changes from 6 commits
956a4b5
25c5e87
7e17fee
9d15eda
8a7e50f
943167f
7fff081
817736e
868d55e
4f395dc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright 2017 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build race | ||
// +build race | ||
|
||
package git | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestRunWithContextNoTimeout(t *testing.T) { | ||
maxLoops := 10 | ||
|
||
// 'git --version' does not block so it must be finished before the timeout triggered. | ||
cmd := NewCommand(context.Background(), "--version") | ||
for i := 0; i < maxLoops; i++ { | ||
if err := cmd.RunWithContext(&RunContext{}); err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
} | ||
|
||
func TestRunWithContextTimeout(t *testing.T) { | ||
maxLoops := 10 | ||
|
||
// 'git hash-object --stdin' blocks on stdin so we can have the timeout triggered. | ||
cmd := NewCommand(context.Background(), "hash-object", "--stdin") | ||
for i := 0; i < maxLoops; i++ { | ||
if err := cmd.RunWithContext(&RunContext{Timeout: 1 * time.Millisecond}); err != nil { | ||
if err != context.DeadlineExceeded { | ||
t.Fatalf("Testing %d/%d: %v", i, maxLoops, err) | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,29 @@ | ||
// Copyright 2017 The Gitea Authors. All rights reserved. | ||
// Copyright 2022 The Gitea Authors. All rights reserved. | ||
// Use of this source code is governed by a MIT-style | ||
// license that can be found in the LICENSE file. | ||
|
||
//go:build race | ||
// +build race | ||
|
||
package git | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
"time" | ||
) | ||
|
||
func TestRunInDirTimeoutPipelineNoTimeout(t *testing.T) { | ||
maxLoops := 1000 | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
// 'git --version' does not block so it must be finished before the timeout triggered. | ||
func TestRunWithContextStd(t *testing.T) { | ||
cmd := NewCommand(context.Background(), "--version") | ||
for i := 0; i < maxLoops; i++ { | ||
if err := cmd.RunInDirTimeoutPipeline(-1, "", nil, nil); err != nil { | ||
t.Fatal(err) | ||
} | ||
} | ||
} | ||
|
||
func TestRunInDirTimeoutPipelineAlwaysTimeout(t *testing.T) { | ||
maxLoops := 1000 | ||
stdout, stderr, err := cmd.RunWithContextString(&RunContext{}) | ||
assert.NoError(t, err) | ||
assert.Empty(t, stderr) | ||
assert.Contains(t, stdout, "git version") | ||
|
||
// 'git hash-object --stdin' blocks on stdin so we can have the timeout triggered. | ||
cmd := NewCommand(context.Background(), "hash-object", "--stdin") | ||
for i := 0; i < maxLoops; i++ { | ||
if err := cmd.RunInDirTimeoutPipeline(1*time.Microsecond, "", nil, nil); err != nil { | ||
if err != context.DeadlineExceeded { | ||
t.Fatalf("Testing %d/%d: %v", i, maxLoops, err) | ||
} | ||
} | ||
cmd = NewCommand(context.Background(), "--no-such-arg") | ||
stdout, stderr, err = cmd.RunWithContextString(&RunContext{}) | ||
if assert.Error(t, err) { | ||
assert.Equal(t, stderr, err.Stderr()) | ||
assert.Contains(t, err.Stderr(), "unknown option:") | ||
assert.Contains(t, err.Error(), "exit status 129 - unknown option:") | ||
assert.Empty(t, stdout) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -86,6 +86,10 @@ func (pm *Manager) AddContext(parent context.Context, description string) (ctx c | |||
// Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the | ||||
// process table. | ||||
func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) { | ||||
if timeout <= 0 { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems like there are some user-defined timeouts being passed to this function, I'm not sure that in the middle of normal operations a panic should occur from a process... Reference: gitea/services/mailer/mailer.go Line 276 in a82fd98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hm that would mean we either should fail on gitea init or put out a warn and ignore this setting There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When passing timeout<=0 into AddContextTimeout:
From my experience, in a big and complex system, everything should be defined as a clear behavior, if there is something unexpected happening, the error should be reported in the first time. We should always expose mistakes, instead of hiding mistakes. So I think this new mechanism is better and more stable, this refactoring is only done in 1.17 and we have enough time to catch more bugs in future. And fortunately, the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or we could ignore the timeout context if timeout <= 0 but not panic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, ignoring non-sense values in low-level frameworks is wrong. Many Go functions also panic if there is an invalid input. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think we should prevent such issues from being reported. I'm not sure at which level the context cancelled are being reported to the admin, but otherwise it might be their "first time" that they observe that their configuration is incorrect. I think we should have a little note in the 1.17 announcement that a negative timeout can lead to panics and errors and that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Many were written without being able to return errors, and given they don't want to break backwards compatibility, they instead panic. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why do we need to mention that? If users were using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I have no clue, but from what I've learned is that people can somehow survive with incorrect configurations without noticing. Anyhow, approving. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To be clear: If a user were using If a user were using ps: I agree "people can somehow survive with incorrect configurations without noticing.", however, once they meet some errors and report "bugs", it makes maintainers frustrated ..... nobody can guess what's wrong on user side if mistakes are hidden. We really need a clear system 😊 |
||||
// it's meaningless to use timeout <= 0, and it must be a bug! so we must panic here to tell developers to make the timeout correct | ||||
panic("the timeout must be greater than zero, otherwise the context will be cancelled immediately") | ||||
} | ||||
ctx, cancel = context.WithTimeout(parent, timeout) | ||||
|
||||
ctx, pid, finshed := pm.Add(ctx, description, cancel) | ||||
|
@@ -239,7 +243,7 @@ func (pm *Manager) ExecDirEnv(ctx context.Context, timeout time.Duration, dir, d | |||
// Returns its complete stdout and stderr | ||||
// outputs and an error, if any (including timeout) | ||||
func (pm *Manager) ExecDirEnvStdIn(ctx context.Context, timeout time.Duration, dir, desc string, env []string, stdIn io.Reader, cmdName string, args ...string) (string, string, error) { | ||||
if timeout == -1 { | ||||
if timeout <= 0 { | ||||
timeout = 60 * time.Second | ||||
} | ||||
|
||||
|
Uh oh!
There was an error while loading. Please reload this page.